我刚开始使用的是参考文章1中最后成功的代码,但是在我的代码中,下面的不起作用。
一、使用代码配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.cors.CorsConfiguration;import org.springframework.web.cors.reactive.CorsWebFilter;import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;import org.springframework.web.util.pattern.PathPatternParser;@Configuration public class WebConfig { @Bean public CorsWebFilter corsFilter () { CorsConfiguration config = new CorsConfiguration (); config.addAllowedMethod("*" ); config.addAllowedOrigin("*" ); config.addAllowedHeader("*" ); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource (new PathPatternParser ()); source.registerCorsConfiguration("/**" , config); return new CorsWebFilter (source); } }
二、使用application.yml配置 使用了application.yml还是无效
1 2 3 4 5 6 7 8 9 10 spring: cloud: gateway: globalcors: cors-configurations: '[/**]' : allowCredentials: true allowedOrigins: "*" allowedMethods: "*" allowedHeaders: "*"
三、使用Security的方式进行配置 因为我的网关集成了Spring Security,所以需要单独的配置
(1) 实现CorsFilter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import org.springframework.context.annotation.Configuration;import org.springframework.http.HttpHeaders;import org.springframework.http.HttpMethod;import org.springframework.http.HttpStatus;import org.springframework.http.server.reactive.ServerHttpRequest;import org.springframework.http.server.reactive.ServerHttpResponse;import org.springframework.web.cors.reactive.CorsUtils;import org.springframework.web.server.ServerWebExchange;import org.springframework.web.server.WebFilter;import org.springframework.web.server.WebFilterChain;import reactor.core.publisher.Mono;public class CorsFilter implements WebFilter { @Override public Mono<Void> filter (ServerWebExchange ctx, WebFilterChain chain) { ServerHttpRequest request = ctx.getRequest(); if (CorsUtils.isCorsRequest(request)) { ServerHttpResponse response = ctx.getResponse(); HttpHeaders headers = response.getHeaders(); headers.set(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*" ); headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "*" ); headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "" ); headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "false" ); headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*" ); headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "3600" ); if (request.getMethod() == HttpMethod.OPTIONS) { response.setStatusCode(HttpStatus.OK); return Mono.empty(); } } return chain.filter(ctx); } }
(2) 就是在Security中配置跨域
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Bean public SecurityWebFilterChain springSecurityFilterChain (ServerHttpSecurity http) { http.oauth2ResourceServer().jwt().jwtAuthenticationConverter(jwtAuthenticationConverter()); http.authorizeExchange() .pathMatchers(ArrayUtil.toArray(ignoreUrlsConfig.getUrls(),String.class)).permitAll() .anyExchange().access(authorizationManager) .and().exceptionHandling() .accessDeniedHandler(restfulAccessDeniedHandler) .authenticationEntryPoint(restAuthenticationEntryPoint) .and().csrf().disable(); http.addFilterAt(new CorsFilter (), SecurityWebFiltersOrder.SECURITY_CONTEXT_SERVER_WEB_EXCHANGE); return http.build(); }
问题 根据测试,实现了WebFilter接口,就算不写http.addFilterAt,也还是会进入到过滤器CorsFilter内部的。写了之后,过滤器会执行两遍,出现两个重复的请求头。
解决办法就是把上面的CorsFilter这个filter上的@Configuration和@Component都去掉,然后就可以使用 http.addFilterAt() 注入一次Filter了。这样也不需要使用CorsConfigurationSource这个Bean了,下面的奇怪的几个问题,自然就解决了。
例外的问题wildcard ‘*’ 上两步做完了,在前台使用axios进行请求,还是会出现问题:The value of the ‘Access-Control-Allow-Origin’ header in the response must not be the wildcard ‘*’ when the request’s credentials mode is ‘include’. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
当前台axios设置了withCredentials: true,这个时候Access-Control-Allow-Origin不能指定为*,要定义为具体的地址,同时定义:ACCESS_CONTROL_ALLOW_CREDENTIALS为true。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Configuration public class CorsFilter implements WebFilter { @Override public Mono<Void> filter (ServerWebExchange ctx, WebFilterChain chain) { ServerHttpRequest request = ctx.getRequest(); HttpHeaders requestHaders=request.getHeaders(); String origin=requestHaders.getOrigin(); if (CorsUtils.isCorsRequest(request)) { ServerHttpResponse response = ctx.getResponse(); HttpHeaders headers = response.getHeaders(); headers.set(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, origin); headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "*" ); headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "" ); headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true" ); headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*" ); headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "3600" ); if (request.getMethod() == HttpMethod.OPTIONS) { response.setStatusCode(HttpStatus.OK); return Mono.empty(); } } return chain.filter(ctx); } }
注意 在开发的时候虽然可以这么设置跨域,但是最新的Chrome浏览器升级了同源策略,设置了SameSite属性:A cookie associated with a cross-site resource at xxx was set without the SameSite
attribute. A future release of Chrome will only deliver cookies with cross-site requests if they are set with SameSite=None
and Secure
. You can review cookies in developer tools under Application>Storage>Cookies and see more details at https://www.chromestatus.com/feature/5088147346030592 and https://www.chromestatus.com/feature/5633521622188032
.
这个的解决方法就是只能设置同一个域名进行请求了,或者是设置SameSite=None
and Secure
,也就是使用https传输。
参考文章:
1.
axios的cookie跨域以及相关配置 (这里有部分的说明,就是说:Access-Control-Allow-Origin不可以为 ‘
‘,因为 ‘ ‘ 会和 Access-Control-Allow-Credentials:true 冲突,需配置指定的地址)
~~ ## 奇怪的问题 (1) 奇怪的问题,就是谷歌浏览器有时候会发送相关的OPTIONS请求,有时候不会(同样的代码)。
(3) 另外一个就是在使用上面的CorsFilter代码解决了跨域配置之后,在有些请求中会生效,但是在post请求中就不会生效了,这让我极度的郁闷。
于是我只能使用 Spring Boot 中三种跨域场景总结 定义的第三种方式进行尝试,两者相加,最后配成了一个跨域的设置.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @Bean public SecurityWebFilterChain springSecurityFilterChain (ServerHttpSecurity http) { http.oauth2ResourceServer().jwt().jwtAuthenticationConverter(jwtAuthenticationConverter()); http.oauth2ResourceServer().authenticationEntryPoint(restAuthenticationEntryPoint); http.cors() .configurationSource(corsConfigurationSource()) .and() .authorizeExchange() .pathMatchers(ArrayUtil.toArray(ignoreUrlsConfig.getUrls(),String.class)).permitAll() .anyExchange().access(authorizationManager) .and().exceptionHandling() .accessDeniedHandler(restfulAccessDeniedHandler) .authenticationEntryPoint(restAuthenticationEntryPoint) .and().csrf().disable(); return http.build(); } @Bean CorsConfigurationSource corsConfigurationSource () { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource (); CorsConfiguration configuration = new CorsConfiguration (); configuration.setAllowCredentials(true ); configuration.setAllowedOrigins(Arrays.asList("*" )); configuration.setAllowedMethods(Arrays.asList("*" )); configuration.setAllowedHeaders(Arrays.asList("*" )); configuration.setMaxAge(Duration.ofHours(1 )); source.registerCorsConfiguration("/**" ,configuration); return source; }
(3) 但是奇怪的问题还是来了,就是上面一顿猛操作之后,当浏览器发出一次请求的时候,必须注释掉CorsConfigurationSource这个bean,只留下filter,但是如果浏览器发送了两次请求的时候,也就是携带了OPTION请求的时候,就需要打开这个注释,暂时还不知道什么原因,难道是我的代码有问题吗?(只能这么认为了)
上面的这两个请求就是出现了问题,下面的是OPTIONS请求可以通过,上面的请求就通不过,很蛋疼。~~