RestTemplate發(fā)送請(qǐng)求時(shí)Cookie的影響及注意事項(xiàng)說明
背景
一個(gè)基于 SpringCloud 的多服務(wù)項(xiàng)目中,服務(wù)間調(diào)用通過 Spring 的 RestTemplate 實(shí)現(xiàn),后臺(tái)模塊 A 有一個(gè)定期清理無效業(yè)務(wù)數(shù)據(jù)的任務(wù),它調(diào)用 Web 服務(wù) B 的 API 時(shí),竟然一直出現(xiàn) Token 已過期問題。
技術(shù)背景
- Web 服務(wù)權(quán)限認(rèn)證使用 Token ,登錄校驗(yàn)成功后,刷新 Token 并調(diào)用 response.addCookie 返回給調(diào)用方——瀏覽器或者內(nèi)部服務(wù)。
- Web 服務(wù)的攔截器,它提取 Token 的順序是:請(qǐng)求參數(shù)、Cookie、Header,然后驗(yàn)證 Token 的有效性。校驗(yàn)通過,刷新 Token 到響應(yīng)對(duì)象的 Cookie 中。
- Token 有效期限 30 分鐘。
- 后臺(tái)定時(shí)任務(wù)模塊 A 每小時(shí)調(diào)用一次模塊 B 的的 API 。
對(duì)于 Web 服務(wù)模塊 B 來說,請(qǐng)求來源有用戶瀏覽器和內(nèi)部服務(wù) A ,Token 校驗(yàn)通過后會(huì)刷新并設(shè)置響應(yīng)對(duì)象的 Cookie 中,而 Cookie 又會(huì)在下次請(qǐng)求時(shí)發(fā)送給服務(wù)器。
各服務(wù)穩(wěn)定運(yùn)行后,定時(shí)任務(wù) A 調(diào)用服務(wù) B 的 API,從來都沒有成功過。
后臺(tái)服務(wù)調(diào)用時(shí) Cookie 失效問題
問題描述
后臺(tái)服務(wù) A 在調(diào)用 B 的 某API 時(shí),雖然會(huì)生成新 Token 信息并設(shè)置到頭域,但是除了應(yīng)用啟動(dòng)時(shí)成功調(diào)用了一次,其他周期的調(diào)用都出現(xiàn)了 Token 已過期問題。
問題分析
分析整個(gè)認(rèn)證過程,發(fā)現(xiàn)在 Web 的攔截器中,如果檢測(cè)到了內(nèi)部服務(wù)調(diào)用,會(huì)刷新 Token 信息并返回給 RestTemplate 對(duì)象,而且攔截器提取 Token 的順序是:請(qǐng)求參數(shù)、Cookie、Header。
// 請(qǐng)求參數(shù) String token = request.getParameter("t"); // Cookie if (StringUtils.isEmpty(token)) { ? ? Cookie[] cs = request.getCookies(); ? ? if (cs != null) { ? ? ? ? for (Cookie c : cs) { ? ? ? ? ? ? if ("t".equals(c.getName())) { ? ? ? ? ? ? ? ? token = c.getValue(); ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? } ? ? ? ? } ? ? } } ?//Header if (StringUtils.isEmpty(token)) { ? ? token = request.getHeader("t"); }
問題癥結(jié)
后臺(tái)模塊 A 一小時(shí)執(zhí)行一次,每次調(diào)用使用 Spring 托管的 RestTemplate 單例對(duì)象發(fā)送請(qǐng)求給 B 時(shí),它包含了上次請(qǐng)求收到的 Cookie 信息。
雖然同時(shí)生成了 Token 的頭域信息,但是在 Web 端優(yōu)先校驗(yàn)了 Cookie ,因此認(rèn)證結(jié)果始終是無效 Token ,請(qǐng)求被拒絕。
啟示錄
我從互聯(lián)網(wǎng)上得到的最好的經(jīng)驗(yàn)之一,就是永遠(yuǎn)不要復(fù)制和粘貼不是自己編寫的代碼。
如果你一定要復(fù)制,那就照著它逐字輸入,逼著自己思考,這些代碼實(shí)際上是什么意思。
這是昨天看科技周刊印象深刻的一句話,本文的問題雖然不是復(fù)制粘貼導(dǎo)致的,但它別人的代碼、別人的思想,我沒有深刻分析過。
當(dāng)我臨時(shí)救火被分派解決這個(gè)問題時(shí),簡(jiǎn)單看了下代碼分析如下:
- 后端服務(wù)設(shè)置 Token 到了頭域
- 而 Web 模塊只從 Cookie 和請(qǐng)求參數(shù)中獲取 Token ,沒有從 Header 中獲取
想當(dāng)然地以為,只要加上從 Header 中獲取就好了,而且陰差陽錯(cuò)的加在了 Cookie 獲取的后面,所以問題還是沒有解決。
反復(fù)加日志,打印各個(gè)信息的 Token ,發(fā)現(xiàn)解析時(shí)用的 Token 跟頭域不一樣, Token 失效時(shí)間也很規(guī)律,就是上次定時(shí)任務(wù)的調(diào)用時(shí)間。突然意識(shí)到了,Web 服務(wù)用的 Token 跟我想的不一樣,誰把 Token 給改了?
答案是 Cookie,RestTemplate 竟然在發(fā)送請(qǐng)求時(shí)把上次的 Cookie 給帶上了。30分鐘的有效期,下一輪定時(shí)任務(wù)執(zhí)行時(shí),早就是失效了啊。
RestTemplate 注意事項(xiàng)
用 RestTemplate 進(jìn)行服務(wù)調(diào)用時(shí),最好清掉 Cookie 信息,
一來避免本文出現(xiàn)的情況;
二來,它作為Spring 托管的單例,如果訪問的是不同系統(tǒng)的 API ,勢(shì)必會(huì)出現(xiàn) Cookie 混淆、失效的問題!
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
springboot集成mybatisplus的詳細(xì)步驟
MyBatis-Plus (opens new window)(簡(jiǎn)稱 MP)是一個(gè) MyBatis (opens new window)的增強(qiáng)工具,在 MyBatis 的基礎(chǔ)上只做增強(qiáng)不做改變,為簡(jiǎn)化開發(fā)、提高效率而生,這篇文章主要介紹了springboot四步集成mybatisplus,需要的朋友可以參考下2022-10-10SpringBoot遠(yuǎn)程訪問redis服務(wù)器問題剖析
使用了SpringBoot的項(xiàng)目,在遠(yuǎn)程連接Redis服務(wù)器時(shí),會(huì)遇倒一些小問題,下面通過本文給大家全面解析SpringBoot遠(yuǎn)程訪問redis服務(wù)器問題,需要的朋友參考下吧2017-04-04Java Socket實(shí)現(xiàn)聊天室附1500行源代碼
Socket是應(yīng)用層與TCP/IP協(xié)議族通信的中間軟件抽象層,它是一組接口。本篇文章手把手帶你通過Java Socket來實(shí)現(xiàn)自己的聊天室,大家可以在過程中查缺補(bǔ)漏,溫故而知新2021-10-10spring boot 配置freemarker及如何使用freemarker渲染頁(yè)面
springboot中自帶的頁(yè)面渲染工具為thymeleaf 還有freemarker這兩種模板引擎,本文重點(diǎn)給大家介紹spring boot 配置freemarker及如何使用freemarker渲染頁(yè)面,感興趣的朋友一起看看吧2023-10-10深入淺出探究Java多態(tài)的實(shí)現(xiàn)和應(yīng)用
多態(tài)是實(shí)現(xiàn)面向?qū)ο蟮能浖夹g(shù)中必不可少的一個(gè)內(nèi)容,所以這篇文章主要來和大家講解一下Java多態(tài)的實(shí)現(xiàn)和應(yīng)用,感興趣的小伙伴可以了解一下2023-07-07Spring集成MyBatis?及Aop分頁(yè)的實(shí)現(xiàn)代碼
這篇文章主要介紹了Spring集成MyBatis?及Aop分頁(yè)的實(shí)現(xiàn),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-04-04SpringBoot?整合ChatGPT?API項(xiàng)目實(shí)戰(zhàn)教程
這篇文章主要介紹了SpringBoot整合ChatGPT API項(xiàng)目實(shí)戰(zhàn)教程,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-05-05spring使用validation參數(shù)及全局異常檢測(cè)方式
本文主要介紹了Java的數(shù)據(jù)校驗(yàn)規(guī)范validation-api,包括其定義的注解和HibernateValidator的實(shí)現(xiàn),還介紹了spring-boot-starter-validation的使用,可以讓開發(fā)者在SpringBoot應(yīng)用中簡(jiǎn)化數(shù)據(jù)校驗(yàn)的功能2025-02-02openEuler?搭建java開發(fā)環(huán)境的詳細(xì)過程
這篇文章主要介紹了openEuler?搭建java開發(fā)環(huán)境,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06