跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。
例如:a页面想获取b页面资源,如果a、b页面的协议、域名、端口、子域名不同,所进行的访问行动都是跨域的,而浏览器为了安全问题一般都限制了跨域访问,也就是不允许跨域请求资源。注意:跨域限制访问,其实是浏览器的限制。理解这一点很重要!!!
同源策略:是指协议,域名,端口都要相同,其中有一个不同都会产生跨域;
Sameoriginpolicy
)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。不同源的应用场景:
axios
发起的POST
请求
Access to XMLHttpRequest at 'http://localhost:8080/login' from origin 'http://localhost:8081'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin'
header is present on the requested resource.
// 在'http://localhost:8080/login'从原点'http://localhost:8081'已被CORS策略阻止:
// 请求的资源上不存在“Access Control Allow Origin”标头
POST http://localhost:8080/login net::ERR_FAILED 200
Uncaught (in promise) AxiosError
{
message: 'Network Error',
name: 'AxiosError',
code: 'ERR_NETWORK',
config: {…},
request: XMLHttpRequest,
......
}
对于 CORS的跨域请求,主要有以下几种方式可供选择:
CorsFilter
WebMvcConfigurer
@CrossOrigin
(HttpServletResponse)
webfilter
实现跨域注意:
CorFilter
、 WebMvConfigurer
、@CrossOrigin
需要SpringMVC 4.2
以上版本才支持,对应springBoot 1.3
版本以上@CrossOrigin
注解来进行细粒度更高的跨域资源控制。所有解决跨域问题,不外乎就是解决浏览器拦截问题,要么前端设置请求头,要么后端设置请求头,无论谁设置请求头,浏览器只要放行即可
CorsFilter
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/**
* 全局跨域配置
*/
@Configuration
public class GlobalCorsConfig {
@Bean
public CorsFilter corsFilter(){
//1. 添加 CORS配置信息
CorsConfiguration config = new CorsConfiguration();
//放行哪些原始域
// springboot 2.4.0 之前使用这个 或直接指定放行的域名http://localhost:8081...
// config.addAllowedOrigin("*");
config.addAllowedOriginPattern("*"); // springboot 2.4.0 之后使用这个
// 是否发送 Cookie
config.setAllowCredentials(true);
// 放行哪些请求方式
config.addAllowedMethod("*");
// 放行哪些原始请求头部信息
config.addAllowedHeader("*");
// 暴露哪些头部信息
config.addExposedHeader("*");
//2. 添加映射路径
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**",config);
//3. 返回新的CorsFilter
return new CorsFilter(source);
}
}
WebMvcConfigurer
import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class GlobalCorsConfig implements WebMvcConfigurer{
@Override
public void addCorsMappings(@NotNull CorsRegistry registry) {
registry.addMapping("/**")
// 是否发送cookie
.allowCredentials(true)
// 放行哪些域名
// springboot 2.4.0 之前使用这个 或直接指定放行的域名http://localhost:8081...
// .allowedOrigins("*")
.allowedOriginPatterns("*") // springboot 2.4.0 之后使用这个
// 允许任何方法
.allowedMethods("GET", "POST", "PUT", "DELETE")
// 允许任何请求头
.allowedHeaders("*")
.exposedHeaders("*")
// 表明在3600秒内,不需要再发送预检验请求
.maxAge(3600L)
;
}
}
在springboot2.x版本以后,点开注解源码可以看到credentials默认是关闭的,该值是一个布尔值,表示是否允许发送 Cookie 。默认情况下, Cookie 不包括在 CORS 请求之中,设置为 true,即表示服务器明确许可, Cookie 可以包含中跨域请求中,一起发送给服务器。这个值也只能设置为 true ,如果服务器不要浏览器发送 Cookie,删除该字段即可。 在1.x版本的
springboo
t中,是以origins
作为参数,而新版本则改为了originPatterns
在控制器(类上)上使用注解 @CrossOrigin:,表示该类的所有方法允许跨域
@RestController
@CrossOrigin(originPatterns = "*",allowCredentials = "true")
public class LoginController {
...
}
在方法上使用注解 @CrossOrigin:
@ApiOperation(value = "登录")
@PostMapping("login")
@CrossOrigin(originPatterns = "*",allowCredentials = "true")
public RespBean login(@RequestBody LoginVo loginVo, HttpServletRequest request){
if (!CaptchaUtil.ver(loginVo.getCode(),request)){
return RespBean.loginCode();
}
CaptchaUtil.clear(request);
return loginService.login(loginVo.getUsername(), loginVo.getPassword());
}
控制器
@ApiOperation(value = "登录")
@PostMapping("login")
public RespBean login(HttpServletRequest request,HttpServletResponse response){
// 调用cors工具类
CorsUtilks.cors(request,response);
...
return loginService.login(loginVo.getUsername(), loginVo.getPassword());
}
Cors
工具类
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 手动设置响应头
*/
public class CorsUtilks {
public static void cors(HttpServletRequest request, HttpServletResponse response){
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin")); // 设置允许所有跨域访问
response.setHeader("Access-Control-Allow-Methods", "POST,GET,PUT,OPTIONS,DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin,X-Requested-With,Content-Type,Accept,Authorization,token");
response.setHeader("Access-Control-Allow-Credentials", "true");
}
}
filter
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 自定义filter实现跨域
*/
@Component
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin")); // 设置允许所有跨域访问
response.setHeader("Access-Control-Allow-Methods", "POST,GET,PUT,OPTIONS,DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin,X-Requested-With,Content-Type,Accept,Authorization,token");
response.setHeader("Access-Control-Allow-Credentials", "true");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void init(FilterConfig filterConfig){}
@Override
public void destroy() {}
}