文章目录
问题描述
- 前后端分离页面,Vue+springboot均配置跨域,并访问成功,session取值正常。
- ngnix代理项目,Vue访问遇到session不一致的问题,session取值为null(使用springSession进行分布式session共享)。
仅在springboot配置跨域
-
访问springboot直接部署的项目(session取值正常)
-
访问ngnix代理的项目(session取值为null)
在springboot和ngnix均配置跨域
- 提示跨域配置重复。
仅在ngnix配置跨域
-
提示跨域失败。此时考虑是ngnix跨域配置问题。
-
更改ngnix跨域配置
# 指定允许跨域的方法,*代表所有
add_header Access-Control-Allow-Methods *;
# 预检命令的缓存,如果不缓存每次会发送两次请求
add_header Access-Control-Max-Age 3600;
# 带cookie请求需要加上这个字段,并设置为true
add_header Access-Control-Allow-Credentials true;
# 表示允许这个域跨域调用(客户端发送请求的域名和端口)
# $http_origin动态获取请求客户端请求的域 不用*的原因是带cookie的请求不支持*号
add_header Access-Control-Allow-Origin $http_origin;
# 表示请求头的字段 动态获取
add_header Access-Control-Allow-Headers
$http_access_control_request_headers;
# OPTIONS预检命令,预检命令通过时才发送请求
# 检查请求的类型是不是预检命令
if ($request_method = OPTIONS){
return 200;
}
- 此时跨域配置成功,但sessionId仍不一致,取值为null。
是因为withCredentials设置为true,Origin不能为*?
- 测试后发现,即使Origin不为*,sessionId同样不一致。
猜想为cookie的samesite的问题
- 通过查阅此文:使用springSession解决分布式session共享问题时,cookie新增了 SameSite这个字段,所以不能携带cookie进行跨域post访问。
服务端返回给客户端的set-cookie中带有samesite=lax,这就是问题的根源,它表示不能携带cookie进行跨域post访问,然而我们是需要携带cookie的。
解决办法
- 配置bean取消cookie的samesite设置。
@Bean
public CookieSerializer httpSessionIdResolver() {
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
// 取消仅限同一站点设置
cookieSerializer.setSameSite(null);
return cookieSerializer;
}
- 至此,sessionId一致,取值成功。
总结
- 使用ngnix代理项目时,跨域配置springboot和ngnix只用配置任意一方,都配的话会报错。
- springboot配置推荐使用实现filter的方式(可避免origin为*的问题)
@Component
public class MyCrosFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 预检请求,请求头Origin是客户端地址,要求跨域头不能是*
String origin = httpRequest.getHeader("Origin");
if (origin == null) {
httpResponse.addHeader("Access-Control-Allow-Origin", "*");
} else {
httpResponse.addHeader("Access-Control-Allow-Origin", origin);
}
httpResponse.addHeader("Access-Control-Allow-Headers",
"Origin, x-requested-with, Content-Type, Accept,X-Cookie,token");
//httpResponse.addHeader("Access-Control-Expose-Headers", "token");
httpResponse.addHeader("Access-Control-Allow-Credentials", "true");
httpResponse.addHeader("Access-Control-Allow-Methods", "GET,POST,PUT,OPTIONS,DELETE");
//预检请求,直接通过。
if (httpRequest.getMethod().equals("OPTIONS")) {
httpResponse.setStatus(HttpServletResponse.SC_OK);
return;
}
chain.doFilter(request, response);
} catch (Exception e) {
throw e;
}
}
@Override
public void destroy() {
}
}
-
ngnix的跨域配置见上文。
-
跨域成功但是sessionId不一致的情况,查看响应头中的set-cookie是否设置了samesite,如果有,则配置bean取消cookie的samesite设置,配置方式见上文。