springboot?web項(xiàng)目中?Set-Cookie?失敗原因及解決辦法
1. 背景
目前有個(gè)項(xiàng)目 線上環(huán)境 使用spring session管理的登錄
項(xiàng)目中有兩個(gè)接口
一個(gè)用來登錄的 登錄成功后會(huì)設(shè)置cookie 后續(xù)請(qǐng)求就會(huì)使用該cookie (cookie的鍵值就是session Id 和 登錄后的信息 例如菜單,權(quán)限等)
一個(gè)用來檢查是否登錄的 根據(jù)session id 來判斷是否登錄 沒有登錄信息的 直接返回未登錄
調(diào)用登錄成功后 發(fā)現(xiàn)Set-Cookie 出現(xiàn)響應(yīng)頭中 但是 調(diào)用檢查接口發(fā)現(xiàn)還是還是未登錄
如下圖 模擬登錄接口 響應(yīng)中正確返回了 session

但是檢查登錄卻未將 模擬登陸返回的接口攜帶上 導(dǎo)致登錄校驗(yàn)失敗

2. 問題排查
剛開始懷疑兩次請(qǐng)求的session id 不一致導(dǎo)致無法使用 模擬登錄返回的cookie 但是在測(cè)試環(huán)境時(shí) 可以校驗(yàn)登錄接口正常使用 模擬登陸返回的session
模擬登錄的接口

驗(yàn)證登錄的接口

那問題好像不是session不一致的問題。
因?yàn)槟壳笆褂胹pring-session來管理session 如果登錄成功后 會(huì)將session id 以及登錄信息 設(shè)置到cookie 同時(shí)存入到瀏覽器 應(yīng)用的 cookie中 同時(shí)會(huì)將session 存入到 redis中 其他的接口就會(huì)攜帶
那目前設(shè)置沒問題 那登錄成功后是否 將session 存入到到瀏覽器應(yīng)用的cookie 直接去檢查 發(fā)現(xiàn)沒有設(shè)置上

這里發(fā)現(xiàn) 模擬登錄成功后 并沒有將session 存入到 瀏覽器應(yīng)用的cookie中
那么問題就變成了 為什么 沒有將session 存入到 瀏覽器應(yīng)用的cookie中
接下來就是一步步的修改
首先可以明確的是 頁(yè)面部署在A域名 接口部署在B域名 這種請(qǐng)求肯定是跨域的 所以我們要在接口的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;
#代理的配置 這個(gè)是虛擬網(wǎng)關(guān)的配置 每個(gè)項(xiàng)目不一定相同
proxy_pass http://gateway;
}修改過程中 首先將登錄接口中設(shè)置cookie 時(shí)增加sameSite 參數(shù) 并設(shè)置值為None 表示 允許在跨站點(diǎn)請(qǐng)求中發(fā)送
public static void setCookie(HttpServletResponse response, String cookieName, String value, int maxAgeExpiry) {
//設(shè)置cookie 默認(rèn)7天
ResponseCookie cookie = ResponseCookie.from(cookieName, value)
.httpOnly(true) // 禁止js讀取
.secure(true) // 在http下也傳輸
.path("/") // path
.maxAge(maxAgeExpiry)
// 新增加的部分 允許在跨站點(diǎn)請(qǐng)求中發(fā)送
.sameSite("None")
.build()
;
// 設(shè)置Cookie Header
response.setHeader(HttpHeaders.SET_COOKIE, cookie.toString());
log.info("設(shè)置cookie完成,{}: {}", cookieName, cookie);
}- 其次在前端 vue的頁(yè)面調(diào)用接口時(shí)增加 使用憑證參數(shù) 由于使用的時(shí) vue的 axios 請(qǐng)求框架 直接在項(xiàng)目中增加 配置即可 axios.defaults.withCredentials = true;
其他的方式 添加方式如下
使用vue.resource發(fā)送請(qǐng)求時(shí)配置如下:
main.js中 Vue.http.options.xhr = { withCredentials: true }
使用vue.axios發(fā)送請(qǐng)求時(shí)配置如下:
axios.defaults.withCredentials = true;
jquery請(qǐng)求帶上 xhrFields: {withCredentials: true}, crossDomain: true;
$.ajax({
type: "post",
url: "",
xhrFields: {withCredentials: true},
crossDomain: true,
data: {username:$("#username").val()},
dataType: "json",
success: function(data){ }
});最后在接口的NGINX配置中同樣添加使用憑證參數(shù)
add_header 'Access-Control-Allow-Credentials' 'true';
本來以為添加完 應(yīng)該可以正常請(qǐng)求了 但是 比之前錯(cuò)誤更明顯了 接口都無法調(diào)用了

結(jié)果一看前端頁(yè)面的控制臺(tái) 看到如下錯(cuò)誤 更有點(diǎn)百思不得其解
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.
居然提示請(qǐng)求頭不被允許 但是我的接口 nginx配置
add_header 'Access-Control-Allow-Headers '*';
這個(gè)配置意思 應(yīng)該是允許所有的請(qǐng)求頭吧 但是查詢了下,看下ai的解釋
這個(gè)錯(cuò)誤提示是因?yàn)樵谶M(jìn)行跨域請(qǐng)求時(shí),瀏覽器在發(fā)送實(shí)際請(qǐng)求之前會(huì)先發(fā)送一個(gè)預(yù)檢請(qǐng)求(OPTIONS請(qǐng)求),這個(gè)請(qǐng)求包含了實(shí)際請(qǐng)求的所有信息,包括請(qǐng)求頭。服務(wù)器在接收到這個(gè)預(yù)檢請(qǐng)求后,需要返回一個(gè)響應(yīng),告訴瀏覽器哪些請(qǐng)求頭是允許的。
在你的配置中,add_header ‘Access-Control-Allow-Headers’ ‘*’;設(shè)置的是允許所有請(qǐng)求頭,但是瀏覽器在發(fā)送實(shí)際請(qǐng)求時(shí),可能只發(fā)送了部分請(qǐng)求頭,所以服務(wù)器需要明確指定允許哪些請(qǐng)求頭。
你可以通過add_header ‘Access-Control-Allow-Headers’ ‘Content-Type, Authorization’;來指定允許的請(qǐng)求頭為 “Content-Type” 和 “Authorization”。這樣,瀏覽器在發(fā)送實(shí)際請(qǐng)求時(shí),只有這兩個(gè)請(qǐng)求頭會(huì)被發(fā)送,服務(wù)器在接收到這個(gè)請(qǐng)求后,也會(huì)只檢查這兩個(gè)請(qǐng)求頭,就不會(huì)出現(xiàn)這個(gè)錯(cuò)誤了。
所以我們?cè)俅涡薷恼?qǐng)求的配置
add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept, Authorization, Accept-Language';
然后就大家都?xì)g喜,校驗(yàn)登錄的接口 也可以正常使用模擬登錄的session ,模擬登錄的session也存入到了應(yīng)用的cookie里
突然在測(cè)試某個(gè)功能 更新接口 居然提示跨域請(qǐng)求 一看控制臺(tái)發(fā)現(xiàn) put 方法不被允許 報(bào)錯(cuò)詳細(xì)如下
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 問題 解決完成
最后來匯總下解決辦法
接口的nginx 跨域配置 例如 headers和 method 中的* 配置修改為具體的
#xx為接口前綴 請(qǐng)注意配置 如果沒有前綴可以放在 / 下
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;
}接口中設(shè)置cookies的方法 增加 samsite屬性 且 屬性值為None
public static void setCookie(HttpServletResponse response, String cookieName, String value, int maxAgeExpiry) {
//設(shè)置cookie 默認(rèn)7天
ResponseCookie cookie = ResponseCookie.from(cookieName, value)
.httpOnly(true) // 禁止js讀取
.secure(true) // 在http下也傳輸
.path("/") // path
.maxAge(maxAgeExpiry)
// 新增加的部分 允許在跨站點(diǎn)請(qǐng)求中發(fā)送
.sameSite("None")
.build()
;
// 設(shè)置Cookie Header
response.setHeader(HttpHeaders.SET_COOKIE, cookie.toString());
log.info("設(shè)置cookie完成,{}: {}", cookieName, cookie);
}在前端 vue的頁(yè)面調(diào)用接口時(shí)增加 使用憑證參數(shù) 由于使用的時(shí) vue的 axios 請(qǐng)求框架 直接在項(xiàng)目中增加 配置即可 axios.defaults.withCredentials = true;
其他的方式 添加方式如下
使用vue.resource發(fā)送請(qǐng)求時(shí)配置如下:
main.js中 Vue.http.options.xhr = { withCredentials: true }
使用vue.axios發(fā)送請(qǐng)求時(shí)配置如下:
axios.defaults.withCredentials = true;
jquery請(qǐng)求帶上 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 不同問題 前后兩個(gè)請(qǐng)求的session不同 請(qǐng)注意判斷是否 是真的不一致 (例如說 我在登錄接口中 將session設(shè)置成功 并成功保存到了應(yīng)用的cookie中 然后在校驗(yàn)登錄接口傳入的sessionId 卻是另一個(gè) 這種就屬于 sessionID 不一致的 )
- 解決: 這種問題不是很好找 需要排查是代碼問題 還是使用的框架 問題 還是 接口NGINX配置問題
- set-cookie 失效 參考文中解決辦法
- 登錄請(qǐng)求和校驗(yàn)請(qǐng)求 cookie設(shè)置的domin 和 校驗(yàn)請(qǐng)求的domin不同 這種也會(huì)導(dǎo)致檢驗(yàn)請(qǐng)求 無法使用 登錄請(qǐng)求設(shè)置的 cookie 不過這種通常出現(xiàn)在 第三方授權(quán)中

nginx配置的 Access-Control-Allow-Origin 和實(shí)際的 跨域地址不同 這個(gè)在頁(yè)面控制臺(tái)會(huì)報(bào)錯(cuò) 這個(gè)修改辦法 將NGINX配置修改為和實(shí)際請(qǐng)求的地址一樣即可
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
到此這篇關(guān)于springboot web項(xiàng)目中 Set-Cookie 失敗 辦法的文章就介紹到這了,更多相關(guān)springboot 項(xiàng)目Set-Cookie 失敗內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot實(shí)現(xiàn)接口校驗(yàn)簽名調(diào)用的項(xiàng)目實(shí)踐
在以SpringBoot開發(fā)后臺(tái)API接口時(shí),會(huì)存在哪些接口不安全的因素呢?通常如何去解決的呢?本文主要介紹了SpringBoot實(shí)現(xiàn)接口校驗(yàn)簽名調(diào)用的項(xiàng)目實(shí)踐,感興趣的可以了解一下2023-09-09
Java案例之隨機(jī)驗(yàn)證碼功能實(shí)現(xiàn)實(shí)例
本篇文章主要介紹了Java案例之隨機(jī)驗(yàn)證碼功能實(shí)現(xiàn)實(shí)例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-06-06
RepeatSubmit若依框架如何防止表單重復(fù)提交注解
若依框架中的@RepeatSubmit注解用于防止表單重復(fù)提交,通過在控制器方法上添加該注解,并在前端頁(yè)面和JavaScript代碼中實(shí)現(xiàn)雙重校驗(yàn),可以確保同一用戶在短時(shí)間內(nèi)不會(huì)重復(fù)提交相同的表單2024-11-11
Java的idea連接mongodb數(shù)據(jù)庫(kù)的詳細(xì)教程
這篇文章主要介紹了Java的idea連接mongodb數(shù)據(jù)庫(kù)的詳細(xì)教程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11

