目前有个项目 线上环境 使用spring session管理的登录
项目中有两个接口
一个用来登录的 登录成功后会设置cookie 后续请求就会使用该cookie (cookie的键值就是session Id 和 登录后的信息 例如菜单,权限等)
一个用来检查是否登录的 根据session id 来判断是否登录 没有登录信息的 直接返回未登录
调用登录成功后 发现Set-Cookie 出现响应头中 但是 调用检查接口发现还是还是未登录
如下图 模拟登录接口 响应中正确返回了 session
但是检查登录却未将 模拟登陆返回的接口携带上 导致登录校验失败
刚开始怀疑两次请求的session id 不一致导致无法使用 模拟登录返回的cookie 但是在测试环境时 可以校验登录接口正常使用 模拟登陆返回的session
模拟登录的接口
验证登录的接口
那问题好像不是session不一致的问题。
因为目前使用spring-session来管理session 如果登录成功后 会将session id 以及登录信息 设置到cookie 同时存入到浏览器 应用的 cookie中 同时会将session 存入到 redis中 其他的接口就会携带
那目前设置没问题 那登录成功后是否 将session 存入到到浏览器应用的cookie 直接去检查 发现没有设置上
这里发现 模拟登录成功后 并没有将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;
}
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.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){ }
});
add_header 'Access-Control-Allow-Credentials' 'true';
本来以为添加完 应该可以正常请求了 但是 比之前错误更明显了 接口都无法调用了
结果一看前端页面的控制台 看到如下错误 更有点百思不得其解
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 问题 解决完成
#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;
}
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.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 不能生效问题有很多种 包括但不仅限于 如下
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 ! ! !