springboot web项目中 Set-Cookie 失败 办法

1. 背景

目前有个项目 线上环境 使用spring session管理的登录
项目中有两个接口

一个用来登录的 登录成功后会设置cookie 后续请求就会使用该cookie (cookie的键值就是session Id 和 登录后的信息 例如菜单,权限等)

一个用来检查是否登录的 根据session id 来判断是否登录 没有登录信息的 直接返回未登录

调用登录成功后 发现Set-Cookie 出现响应头中 但是 调用检查接口发现还是还是未登录

如下图 模拟登录接口 响应中正确返回了 session
springboot web项目中 Set-Cookie 失败 办法_第1张图片
但是检查登录却未将 模拟登陆返回的接口携带上 导致登录校验失败
springboot web项目中 Set-Cookie 失败 办法_第2张图片

2. 问题排查

刚开始怀疑两次请求的session id 不一致导致无法使用 模拟登录返回的cookie 但是在测试环境时 可以校验登录接口正常使用 模拟登陆返回的session

模拟登录的接口
springboot web项目中 Set-Cookie 失败 办法_第3张图片
验证登录的接口
springboot web项目中 Set-Cookie 失败 办法_第4张图片
那问题好像不是session不一致的问题。
因为目前使用spring-session来管理session 如果登录成功后 会将session id 以及登录信息 设置到cookie 同时存入到浏览器 应用的 cookie中 同时会将session 存入到 redis中 其他的接口就会携带

那目前设置没问题 那登录成功后是否 将session 存入到到浏览器应用的cookie 直接去检查 发现没有设置上

springboot web项目中 Set-Cookie 失败 办法_第5张图片
这里发现 模拟登录成功后 并没有将session 存入到 浏览器应用的cookie中

那么问题就变成了 为什么 没有将session 存入到 浏览器应用的cookie中

接下来就是一步步的修改
首先可以明确的是 页面部署在A域名 接口部署在B域名 这种请求肯定是跨域的 所以我们要在接口的NGINX中添加 跨域配置

#在指定的接口路径中才添加
location /xxxx/ {
        add_header 'Access-Control-Allow-Origin' 'xxx';
        add_header 'Access-Control-Allow-Headers' '*';
        add_header 'Access-Control-Allow-Methods'  '*';
        proxy_set_header   Host             $host;
        proxy_set_header   X-Real-IP        $remote_addr;
        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto  $scheme;
        #代理的配置 这个是虚拟网关的配置 每个项目不一定相同
        proxy_pass    http://gateway;
    }
  • 修改过程中 首先将登录接口中设置cookie 时增加sameSite 参数 并设置值为None 表示 允许在跨站点请求中发送
public static void setCookie(HttpServletResponse response, String cookieName, String value, int maxAgeExpiry) {
    //设置cookie 默认7天
    ResponseCookie cookie = ResponseCookie.from(cookieName, value)
            .httpOnly(true)		// 禁止js读取
            .secure(true)		// 在http下也传输
            .path("/")			// path
            .maxAge(maxAgeExpiry)
            // 新增加的部分 允许在跨站点请求中发送
            .sameSite("None")
            .build()
            ;

    // 设置Cookie Header
    response.setHeader(HttpHeaders.SET_COOKIE, cookie.toString());
    log.info("设置cookie完成,{}: {}", cookieName, cookie);
}
  • 其次在前端 vue的页面调用接口时增加 使用凭证参数 由于使用的时 vue的 axios 请求框架 直接在项目中增加 配置即可 axios.defaults.withCredentials = true;
    其他的方式 添加方式如下
使用vue.resource发送请求时配置如下:
main.js中 Vue.http.options.xhr = { withCredentials: true }
 
 
使用vue.axios发送请求时配置如下:
axios.defaults.withCredentials = true;
 
 
jquery请求带上 xhrFields: {withCredentials: true}, crossDomain: true;
$.ajax({
type: "post",
url: "",
xhrFields: {withCredentials: true},
crossDomain: true,
data: {username:$("#username").val()},
dataType: "json",
success: function(data){ }
});
  • 最后在接口的NGINX配置中同样添加使用凭证参数
add_header 'Access-Control-Allow-Credentials' 'true';

本来以为添加完 应该可以正常请求了 但是 比之前错误更明显了 接口都无法调用了

springboot web项目中 Set-Cookie 失败 办法_第6张图片
结果一看前端页面的控制台 看到如下错误 更有点百思不得其解
Access to XMLHttpRequest at ‘https://xxx/checkAuthorityUpdate’ from origin ‘https://xxx’ has been blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.

居然提示请求头不被允许 但是我的接口 nginx配置

add_header 'Access-Control-Allow-Headers '*';

这个配置意思 应该是允许所有的请求头吧 但是查询了下,看下ai的解释

这个错误提示是因为在进行跨域请求时,浏览器在发送实际请求之前会先发送一个预检请求(OPTIONS请求),这个请求包含了实际请求的所有信息,包括请求头。服务器在接收到这个预检请求后,需要返回一个响应,告诉浏览器哪些请求头是允许的。

在你的配置中,add_header ‘Access-Control-Allow-Headers’ ‘*’;设置的是允许所有请求头,但是浏览器在发送实际请求时,可能只发送了部分请求头,所以服务器需要明确指定允许哪些请求头。

你可以通过add_header ‘Access-Control-Allow-Headers’ ‘Content-Type, Authorization’;来指定允许的请求头为 “Content-Type” 和 “Authorization”。这样,浏览器在发送实际请求时,只有这两个请求头会被发送,服务器在接收到这个请求后,也会只检查这两个请求头,就不会出现这个错误了。

所以我们再次修改请求的配置

add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept, Authorization, Accept-Language';

然后就大家都欢喜,校验登录的接口 也可以正常使用模拟登录的session ,模拟登录的session也存入到了应用的cookie里


突然在测试某个功能 更新接口 居然提示跨域请求 一看控制台发现 put 方法不被允许 报错详细如下
access to XMLHttpRequest at ‘https://xxx/updateAuditResult’ from origin 'https://xxx has been blocked by CORS policy: Method PUT is not allowed by Access-Control-Allow-Methods in preflight response.

接口中的nginx配置

add_header 'Access-Control-Allow-Methods'  '*';

难道又是不允许* 我们改成具体的尝试下看看
修改为具体的method如下

add_header 'Access-Control-Allow-Methods' 'GET,POST,PUT,DELETE';

果然可以了,至此 set-cookie 问题 解决完成

最后来汇总下解决办法

  1. 接口的nginx 跨域配置 例如 headers和 method 中的* 配置修改为具体的
#xx为接口前缀 请注意配置 如果没有前缀可以放在 / 下
location /xxx/ {
        add_header 'Access-Control-Allow-Origin' 'xxx';
        add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept, Authorization, Accept-Language';
        add_header 'Access-Control-Allow-Credentials' 'true';
        add_header 'Access-Control-Allow-Methods' 'GET,POST,PUT,DELETE';
        proxy_set_header   Host             $host;
        proxy_set_header   X-Real-IP        $remote_addr;
        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto  $scheme;
        proxy_pass    http://gateway;
    }
  1. 接口中设置cookies的方法 增加 samsite属性 且 属性值为None
public static void setCookie(HttpServletResponse response, String cookieName, String value, int maxAgeExpiry) {
    //设置cookie 默认7天
    ResponseCookie cookie = ResponseCookie.from(cookieName, value)
            .httpOnly(true)		// 禁止js读取
            .secure(true)		// 在http下也传输
            .path("/")			// path
            .maxAge(maxAgeExpiry)
            // 新增加的部分 允许在跨站点请求中发送
            .sameSite("None")
            .build()
            ;

    // 设置Cookie Header
    response.setHeader(HttpHeaders.SET_COOKIE, cookie.toString());
    log.info("设置cookie完成,{}: {}", cookieName, cookie);
}
  1. 在前端 vue的页面调用接口时增加 使用凭证参数 由于使用的时 vue的 axios 请求框架 直接在项目中增加 配置即可 axios.defaults.withCredentials = true;
    其他的方式 添加方式如下
使用vue.resource发送请求时配置如下:
main.js中 Vue.http.options.xhr = { withCredentials: true }
 
 
使用vue.axios发送请求时配置如下:
axios.defaults.withCredentials = true;
 
 
jquery请求带上 xhrFields: {withCredentials: true}, crossDomain: true;
$.ajax({
type: "post",
url: "",
xhrFields: {withCredentials: true},
crossDomain: true,
data: {username:$("#username").val()},
dataType: "json",
success: function(data){ }
});

另外 登录成功session 不能生效问题有很多种 包括但不仅限于 如下

  • sessionID 不同问题 前后两个请求的session不同 请注意判断是否 是真的不一致 (例如说 我在登录接口中 将session设置成功 并成功保存到了应用的cookie中 然后在校验登录接口传入的sessionId 却是另一个 这种就属于 sessionID 不一致的 )
    解决: 这种问题不是很好找 需要排查是代码问题 还是使用的框架 问题 还是 接口NGINX配置问题
  • set-cookie 失效 参考文中解决办法
  • 登录请求和校验请求 cookie设置的domin 和 校验请求的domin不同 这种也会导致检验请求 无法使用 登录请求设置的 cookie 不过这种通常出现在 第三方授权中
    在这里插入图片描述
  • nginx配置的 Access-Control-Allow-Origin 和实际的 跨域地址不同 这个在页面控制台会报错 这个修改办法 将NGINX配置修改为和实际请求的地址一样即可

Access to XMLHttpRequest at ‘https://xxx/checkAuthorityUpdate’ from origin ‘https://xxx:8068’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: The ‘Access-Control-Allow-Origin’ header has a value ‘https://xxx’ that is not equal to the supplied origin

  • 等等

god day ! ! !

你可能感兴趣的:(java,java,session,cookie)