springboot?web項目中?Set-Cookie?失敗原因及解決辦法
1. 背景
目前有個項目 線上環(huán)境 使用spring session管理的登錄
項目中有兩個接口
一個用來登錄的 登錄成功后會設(shè)置cookie 后續(xù)請求就會使用該cookie (cookie的鍵值就是session Id 和 登錄后的信息 例如菜單,權(quán)限等)
一個用來檢查是否登錄的 根據(jù)session id 來判斷是否登錄 沒有登錄信息的 直接返回未登錄
調(diào)用登錄成功后 發(fā)現(xiàn)Set-Cookie 出現(xiàn)響應(yīng)頭中 但是 調(diào)用檢查接口發(fā)現(xiàn)還是還是未登錄
如下圖 模擬登錄接口 響應(yīng)中正確返回了 session
但是檢查登錄卻未將 模擬登陸返回的接口攜帶上 導(dǎo)致登錄校驗失敗
2. 問題排查
剛開始懷疑兩次請求的session id 不一致導(dǎo)致無法使用 模擬登錄返回的cookie 但是在測試環(huán)境時 可以校驗登錄接口正常使用 模擬登陸返回的session
模擬登錄的接口
驗證登錄的接口
那問題好像不是session不一致的問題。
因為目前使用spring-session來管理session 如果登錄成功后 會將session id 以及登錄信息 設(shè)置到cookie 同時存入到瀏覽器 應(yīng)用的 cookie中 同時會將session 存入到 redis中 其他的接口就會攜帶
那目前設(shè)置沒問題 那登錄成功后是否 將session 存入到到瀏覽器應(yīng)用的cookie 直接去檢查 發(fā)現(xiàn)沒有設(shè)置上
這里發(fā)現(xiàn) 模擬登錄成功后 并沒有將session 存入到 瀏覽器應(yīng)用的cookie中
那么問題就變成了 為什么 沒有將session 存入到 瀏覽器應(yīng)用的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; #代理的配置 這個是虛擬網(wǎng)關(guān)的配置 每個項目不一定相同 proxy_pass http://gateway; }
修改過程中 首先將登錄接口中設(shè)置cookie 時增加sameSite 參數(shù) 并設(shè)置值為None 表示 允許在跨站點(diǎn)請求中發(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)請求中發(fā)送 .sameSite("None") .build() ; // 設(shè)置Cookie Header response.setHeader(HttpHeaders.SET_COOKIE, cookie.toString()); log.info("設(shè)置cookie完成,{}: {}", cookieName, cookie); }
- 其次在前端 vue的頁面調(diào)用接口時增加 使用憑證參數(shù) 由于使用的時 vue的 axios 請求框架 直接在項目中增加 配置即可 axios.defaults.withCredentials = true;
其他的方式 添加方式如下
使用vue.resource發(fā)送請求時配置如下: main.js中 Vue.http.options.xhr = { withCredentials: true } 使用vue.axios發(fā)送請求時配置如下: 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配置中同樣添加使用憑證參數(shù)
add_header 'Access-Control-Allow-Credentials' 'true';
本來以為添加完 應(yīng)該可以正常請求了 但是 比之前錯誤更明顯了 接口都無法調(diào)用了
結(jié)果一看前端頁面的控制臺 看到如下錯誤 更有點(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.
居然提示請求頭不被允許 但是我的接口 nginx配置
add_header 'Access-Control-Allow-Headers '*';
這個配置意思 應(yīng)該是允許所有的請求頭吧 但是查詢了下,看下ai的解釋
這個錯誤提示是因為在進(jìn)行跨域請求時,瀏覽器在發(fā)送實際請求之前會先發(fā)送一個預(yù)檢請求(OPTIONS請求),這個請求包含了實際請求的所有信息,包括請求頭。服務(wù)器在接收到這個預(yù)檢請求后,需要返回一個響應(yīng),告訴瀏覽器哪些請求頭是允許的。
在你的配置中,add_header ‘Access-Control-Allow-Headers’ ‘*’;設(shè)置的是允許所有請求頭,但是瀏覽器在發(fā)送實際請求時,可能只發(fā)送了部分請求頭,所以服務(wù)器需要明確指定允許哪些請求頭。
你可以通過add_header ‘Access-Control-Allow-Headers’ ‘Content-Type, Authorization’;來指定允許的請求頭為 “Content-Type” 和 “Authorization”。這樣,瀏覽器在發(fā)送實際請求時,只有這兩個請求頭會被發(fā)送,服務(wù)器在接收到這個請求后,也會只檢查這兩個請求頭,就不會出現(xiàn)這個錯誤了。
所以我們再次修改請求的配置
add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept, Authorization, Accept-Language';
然后就大家都?xì)g喜,校驗登錄的接口 也可以正常使用模擬登錄的session ,模擬登錄的session也存入到了應(yīng)用的cookie里
突然在測試某個功能 更新接口 居然提示跨域請求 一看控制臺發(fā)現(xiàn) put 方法不被允許 報錯詳細(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為接口前綴 請注意配置 如果沒有前綴可以放在 / 下 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)請求中發(fā)送 .sameSite("None") .build() ; // 設(shè)置Cookie Header response.setHeader(HttpHeaders.SET_COOKIE, cookie.toString()); log.info("設(shè)置cookie完成,{}: {}", cookieName, cookie); }
在前端 vue的頁面調(diào)用接口時增加 使用憑證參數(shù) 由于使用的時 vue的 axios 請求框架 直接在項目中增加 配置即可 axios.defaults.withCredentials = true;
其他的方式 添加方式如下
使用vue.resource發(fā)送請求時配置如下: main.js中 Vue.http.options.xhr = { withCredentials: true } 使用vue.axios發(fā)送請求時配置如下: 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設(shè)置成功 并成功保存到了應(yīng)用的cookie中 然后在校驗登錄接口傳入的sessionId 卻是另一個 這種就屬于 sessionID 不一致的 )
- 解決: 這種問題不是很好找 需要排查是代碼問題 還是使用的框架 問題 還是 接口NGINX配置問題
- set-cookie 失效 參考文中解決辦法
- 登錄請求和校驗請求 cookie設(shè)置的domin 和 校驗請求的domin不同 這種也會導(dǎo)致檢驗請求 無法使用 登錄請求設(shè)置的 cookie 不過這種通常出現(xiàn)在 第三方授權(quán)中
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
到此這篇關(guān)于springboot web項目中 Set-Cookie 失敗 辦法的文章就介紹到這了,更多相關(guān)springboot 項目Set-Cookie 失敗內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot實現(xiàn)接口校驗簽名調(diào)用的項目實踐
在以SpringBoot開發(fā)后臺API接口時,會存在哪些接口不安全的因素呢?通常如何去解決的呢?本文主要介紹了SpringBoot實現(xiàn)接口校驗簽名調(diào)用的項目實踐,感興趣的可以了解一下2023-09-09RepeatSubmit若依框架如何防止表單重復(fù)提交注解
若依框架中的@RepeatSubmit注解用于防止表單重復(fù)提交,通過在控制器方法上添加該注解,并在前端頁面和JavaScript代碼中實現(xiàn)雙重校驗,可以確保同一用戶在短時間內(nèi)不會重復(fù)提交相同的表單2024-11-11Java的idea連接mongodb數(shù)據(jù)庫的詳細(xì)教程
這篇文章主要介紹了Java的idea連接mongodb數(shù)據(jù)庫的詳細(xì)教程,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11