前言
最近在做项目的时候,基于前后端分离的权限管理系统,后台使用 Spring Security 作为权限控制管理, 然后在前端接口访问时候涉及到跨域,但我怎么配置跨域也没有生效,这里有一个坑,在使用Spring Security时候单独配置,SpringBoot 跨越还不行,还需要配置Security 跨域才行。
什么是跨域
跨域是一种浏览器同源安全策略,即浏览器单方面限制脚本的跨域访问
在 HTML 中,<a>, <form>, <img>, <script>, <iframe>, <link> 等标签以及 Ajax 都可以指向一个资源地址,而所谓的跨域请求就是指:当前发起请求的域与该请求指向的资源所在的域不一样。这里的域指的是这样的一个概念:我们认为若协议 + 域名 + 端口号均相同,那么就是同域
当前页面URL和请求的URL首部(端口之前部分)不同则会造成跨域。通俗点讲就是两个URL地址,端口之前部分,只要有一点不同即发生跨域。
例如:
11. 在http://a.baidu.com下访问https://a.baidu.com资源会形成协议跨域。 2 32. 在a.baidu.com下访问b.baidu.com资源会形成主机跨域。 4 53. 在a.baidu.com:80下访问a.baidu.com:8080资源会形成端口跨域。 6
解决跨域的常见方式
JSONP
由于浏览器允许一些带有src属性的标签跨域,常见的有iframe、script、img等,所以JSONP利用script标签可以实习跨域
前端通过script标签请求,并在callback中指定返回的包装实体名称为jsonp(可以自定义)
1<script src="http://aaa.com/getusers?callback=jsonp"></script>
后端将返回结果包装成所需数据格式
1jsonp({ 2 "error":200, 3 "message":"请求成功", 4 "data":[{ 5 "username":"张三", 6 "age":20 7 }] 8})
总结:JSONP实现起来很简单,但是只支持GET请求跨域,存在较大的局限性
CORS
CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing),允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
规范中有一组新增的HTTP首部字段 它通过服务器增加一个特殊的Header[Access-Control-Allow-Origin]来告诉客户端跨域的限制,如果浏览器支持CORS、并且判断Origin通过的话,就会允许XMLHttpRequest发起跨域请求
注意:CORS不支持IE8以下版本的浏览器
| CORS Header属性 | 解释 |
| :--------- | :--: | -----------: |
|Access-Control-Allow-Origin |允许http://www.xxx.com域(自行设置,这里只做示例)发起跨域请求
|Access-Control-Max-Age |设置在86400秒不需要再发送预校验请求
|Access-Control-Allow-Methods |设置允许跨域请求的方法
|Access-Control-Allow-Headers |允许跨域请求包含content-type
|Access-Control-Allow-Credentials| 设置允许Cookie
SpringBoot解决跨越方式
@CrossOrigin 注解
这种是SpringBoot自带的注解,使用非常简单,只需要在对应的接口添加上次注解就行
就表示这个接口支持跨域,其中origins = "*"
表示所有的地址都可以访问这个接口,也可以写具体的地址,表示只有这个地址访问才能访问到接口

可以注解在类上,和方法上,类上表示此controller所有接口都支持跨域,单个方法上表示只有这个接口支持跨域
这种方法虽然优雅简单,但是缺点也不小,需要跨域的接口都需要加上这个注解,这对前后端分离的项目是不友好的,需要添加很多次,所以这种方式基本上用的很少
拦截器方式
重写WebMvcConfigurer的addCorsMappings 方法
1@Configuration 2public class CorsConfig implements WebMvcConfigurer { 3 @Override 4 public void addCorsMappings(CorsRegistry registry) { 5 registry.addMapping("/**")//项目中的所有接口都支持跨域 6 .allowedOriginPatterns("*")//所有地址都可以访问,也可以配置具体地址 7 .allowCredentials(true) 8 .allowedMethods("*")//"GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS" 9 .maxAge(3600);// 跨域允许时间 10 } 11}
注意
allowedOrigins("*")和allowCredentials(true)为true时候会出现错误需要改成allowedOriginPatterns("*")或者单独指定接口allowedOrigins("http//www.baidu.com")
@Configuration 表示是配置类,在项目启动的时候会加载。实现WebMvcConfigurer 接口并重写addCorsMappings 方法。代码比较简单,也有注释
Filter过滤器方式
- 方式一
1@Bean 2 public CorsFilter corsFilter() { 3 //1.添加CORS配置信息 4 CorsConfiguration config = new CorsConfiguration(); 5 //放行哪些原始域 6 config.addAllowedOrigin("*"); 7 //是否发送Cookie信息 8 config.setAllowCredentials(true); 9 //放行哪些原始域(请求方式) 10 config.addAllowedMethod("*"); 11 //放行哪些原始域(头部信息) 12 config.addAllowedHeader("*"); 13 //暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息) 14 config.addExposedHeader("*"); 15 16 //2.添加映射路径 17 UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource(); 18 configSource.registerCorsConfiguration("/**", config); 19 20 //3.返回新的CorsFilter. 21 return new CorsFilter(configSource); 22 }
- 方式二 基于servler 最原始的配置方式
1@Slf4j 2@Component 3public class CorsFilter implements Filter { 4 @Override 5 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 6 HttpServletResponse response = (HttpServletResponse)servletResponse; 7 response.setHeader("Access-Control-Allow-Origin", "*"); 8 response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE"); 9 response.setHeader("Access-Control-Max-Age", "3600"); 10 response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, client_id, uuid, Authorization"); 11 response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); 12 response.setHeader("Pragma", "no-cache"); 13 filterChain.doFilter(servletRequest,response); 14 } 15}
以上三种方法都可以解决问题,最常用的应该是第一种、第二种,控制在自家几个域名范围下足以,一般没必要搞得太细。
这三种配置方式都用了的话,谁生效呢,类似css中样式,就近原则,懂了吧

Spring Security启用CORS支持
如果项目中使用了Spring Security 配置了上面跨越方式还不行,需要单独指定Spring Security跨域
否则跨越不会生效
Spring Security对CORS提供了非常好的支持,只需在配置器中启用CORS支持,并编写一 个CORS配置源即可
1@Override 2 protected void configure(HttpSecurity http) throws Exception { 3 // We don't need CSRF for this example 4 http.cors().and().csrf().disable() 5 // dont authenticate this particular request 6 .authorizeRequests().antMatchers("/", "/*.html", "/favicon.ico", "/css/**", "/js/**", "/fonts/**", "/layui/**", "/img/**", 7 "/v3/api-docs/**", "/swagger-resources/**", "/webjars/**", "/pages/**", "/druid/**", 8 "/statics/**", "/login", "/register").permitAll(). 9 // all other requests need to be authenticated 10 anyRequest().authenticated().and(). 11 // make sure we use stateless session; session won't be used to 12 // store user's state. 13 //覆盖默认登录 14 exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement() 15 // 基于token,所以不需要session 16 .sessionCreationPolicy(SessionCreationPolicy.STATELESS); 17 18 19 // Add a filter to validate the tokens with every request 20 http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); 21 } 22 23 @Bean 24 public CorsFilter corsFilter() { 25 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); 26 27 CorsConfiguration corsConfiguration = new CorsConfiguration(); 28 corsConfiguration.addAllowedOriginPattern("*"); 29 corsConfiguration.addAllowedHeader("*"); 30 corsConfiguration.addAllowedMethod("*"); 31 corsConfiguration.setAllowCredentials(true); 32 source.registerCorsConfiguration("/**", corsConfiguration); 33 return new CorsFilter(source); 34 } 35
