SpringBoot跨域配置
什么是跨域
跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。
例如:a页面想获取b页面资源,如果a、b页面的协议、域名、端口、子域名不同,所进行的访问行动都是跨域的,而浏览器为了安全问题一般都限制了跨域访问,也就是不允许跨域请求资源。注意:跨域限制访问,其实是浏览器的限制。理解这一点很重要!!!
同源策略:是指协议,域名,端口都要相同,其中有一个不同都会产生跨域;
一、同源策略
- 同源,就是咱们域名、端口号、ip、采用的协议都相同,那么我们就是同源的
- 反之就是不同源的!!!
- 出于浏览器的同源策略限制。同源策略(
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跨域
对于 CORS的跨域请求,主要有以下几种方式可供选择:
- 返回新的
CorsFilter
- 重写
WebMvcConfigurer
- 使用注解
@CrossOrigin
- 手动设置响应头
(HttpServletResponse)
- 自定
webfilter
实现跨域
注意:
CorFilter
、WebMvConfigurer
、@CrossOrigin
需要SpringMVC 4.2
以上版本才支持,对应springBoot 1.3
版本以上- 上面前两种方式属于全局 CORS 配置,后两种属于局部 CORS配置。
- 如果使用了局部跨域是会覆盖全局跨域的规则,所以可以通过
@CrossOrigin
注解来进行细粒度更高的跨域资源控制。- 其实无论哪种方案,最终目的都是修改响应头,向响应头中添加浏览器所要求的数据,进而实现跨域
所有解决跨域问题,不外乎就是解决浏览器拦截问题,要么前端设置请求头,要么后端设置请求头,无论谁设置请求头,浏览器只要放行即可
四、全局跨域
1、配置新的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);
}
}
2、重写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)
;
}
}
五、局部跨域
1、注解
在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());
}
2、手动设置响应头
控制器
@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() {}
}