因为前后端分离,采取的前端页面直接调用后端的域名,故存在跨域问题。
首先想到的是直接在nginx层解决,配置如下,用了两种方式:
// 方式一
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Headers' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Content-Type' 'text/plain';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Headers' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Credentials' 'true';
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Headers' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Credentials' 'true';
}
// 方式二:未加if判断
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, PUT, POST, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type,*';
通过浏览器调试发现,浏览器先发送OPTIONS嗅探,然后再真实发送POST请求;
方式二在嗅探请求报错返回403,说明是服务端应用直接拒绝了;
方式一在嗅探时成功返回204,然后浏览器console报错如下:
Access to XMLHttpRequest at 'http://m.sit.xxx.com:8033/gw/test-app/app/inviteRewards/inviteRecordInfoPage' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: 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.
参考博客:segmentfault问答里的解决方法
// 前端加如下代码
withCredentials: false, // 允许携带cookie
然后真的…真的…真的…就解决了~
但是此解决方案略显坎坷,运维需要修改nginx,前端还需加代码,有点复杂。试想还是通过后端代码来解决问题吧~
因为后端服务是springcloud gateway项目
参考博客:https://www.jianshu.com/p/a46e62f9ad1c
官文:https://docs.spring.io/spring-security/site/docs/5.2.0.RC1/reference/htmlsingle/#csrf
最终也解决了问题,代码如下:
@Configuration
public class GwCorsFilter {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
// config.addAllowedMethod("*");
// config.addAllowedOrigin("*");
// config.addAllowedHeader("*");
config.setAllowCredentials(true); // 允许cookies跨域
config.addAllowedOrigin("*");// #允许向该服务器提交请求的URI,*表示全部允许,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin
config.addAllowedHeader("*");// #允许访问的头信息,*表示全部
config.setMaxAge(18000L);// 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
config.addAllowedMethod("OPTIONS");// 允许提交请求的方法类型,*表示全部允许
config.addAllowedMethod("HEAD");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource source =
new org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
最后,感谢以上博客网友提供的帮助。再普及一下跨域的知识,我觉得这篇https://juejin.im/post/5d6e8ae5f265da03ea5a9a5d?utm_source=gold_browser_extension写得不错,建议阅读。