Java 后端 Cookie Session Token會話跟蹤技術

概述
會話從字面理解就是"兩方交流",那問題就來了,HTTP(超文本傳輸協議)里面的"傳輸"不就包含了"兩方交流"的意思嗎?為什么要多此一舉提出會話技術呢?

談到這個,我們就需要先了解 HTTP 協議。HTTP 協議是一中無狀態的協議,它的每次請求都是相互獨立的,那就產生了一個問題,“交流"的另一方,也就是服務器,面對這么多請求,如何判斷每一個請求都是再和誰"交流”?所以這就衍生出了會話技術。

說到這里,那大家都明白了。會話技術就是用來解決"交流"過程中,身份識別的問題。解決了這個問題,才能夠跟蹤和管理用戶狀態信息、提供個性化服務等等。

會話技術發展到現在,主流的是三種方案

Cookie
Session
Token
下面我們依次講解這三個會話跟蹤方案

1.Cookie
首先我們要解決什么 Cookie 這個問題?

Cookie 是一種客戶端會話跟蹤方案,本質就是瀏覽器端的一種“鍵值對”存儲機制,相當于客戶端和服務器之間傳輸的一串字符串。Cookie 存儲在客戶端瀏覽器中。當我們使用 Cookie 來跟蹤會話時,服務器會在 HTTP 響應頭部的set-cookie 字段中設置 Cookie。當客戶端發送后續請求時,會將該 Cookie 信息包含在 HTTP 請求頭中發送給服務器。

那解決信息不共享的問題,我們是不是就可以通過 Cookie 進行解決了。最簡單的實現方式,是不是只需要將身份信息當作 Cookie 就可以了。說到這里相信你你已經很清楚 Cookie 的作用了。

那為了方便在請求中攜帶 Cookie,我們還需要遵循 HTP 協議。Cookie 也是 HTP 協議當中所支持的技術,各大瀏覽器廠商都支持該標準。所以瀏覽器接受到響應回來的 Cookie 時,會自動的將 Cookie 存儲在瀏覽器中,后續的每一次請求中,瀏覽器會自動的將本地存儲的 Cookie 攜帶到服務端中。服務端接收到請求中,可以通過解釋 Cookie,判斷瀛湖的信息了,這樣就解決了在不同的請求中進行身份信息共享的問題。

接下來我們繼續學習如何具體的使用 Cookie 進行身份認證交流

Cookie 認證流程通常包括以下步驟:

用戶訪問需要身份驗證的網站。如果用戶未經過身份驗證,則服務器將重定向用戶到登錄頁面
這部分內容需要另一個技術:統一攔截技術,不是我們本次討論的重點
用戶輸入用戶名和密碼,發送 Http 請求傳遞給服務器進行驗證。
服務器驗證用戶的憑據,并創建一個會話來保存用戶的身份驗證狀態。在這個過程中,服務器生成一個 Cookie,包含了用戶的唯一標識信息
服務器將 Cookie 設置在響應頭中的 set-cookie 字段中,然后發送響應
當用戶發送后續請求時,瀏覽器會將 Cookie 設置請求頭中的 cookie 字段中
服務器解析請求頭中的 Cookie,并解析 Cookie 進行身份的校驗工作

代碼實現

@Slf4j
@RestController
public class SessionController {
// 設置 Cookie
@GetMapping("/c1")
public Result cookie1(HttpServletResponse response){
// 設置 Cookie
response.addCookie(new Cookie(“username”,“AirMan”));
return Result.success();
}
// 獲取 Cookie
@GetMapping("/c2")
public Result cookie2(HttpServletRequest request){
Cookie[] cookies = request.getCookies();
// 校驗 Cookie
for (Cookie cookie : cookies) {
if(cookie.getName().equals(“username”)){
// 輸出 name 為 username 的 cookie
System.out.println("username: " + cookie.getValue());
}
}
return Result.success();
}
}
為什么有了 Cookie 還有 Session,Token 技術的出現呢?那是不是就說明了 Cookie 有一些缺點?

Cookie 最主要的缺點就是移動端 APP(Android、IOS) 中無法使用 Cookie;其次就是 Cookie 不能跨域;還有就是不安全,客戶端可以隨意修改 Cookie 內容,服務端很難保證數據真實性。

什么是跨域?

只要協議不同(http,https),IP 不同,端口不同,滿足其中之一都叫做跨域。

因為現在的項目都是前后端分離的,前端部署在服務器 192.168.150.200 上,端口 80,后端部署在 192.168.150.100上,端口 8080,所以因為跨域的原因,服務器設置的 Cookie 將無法使用。

2.Session
Session 解決了 Cookie 的什么問題?

Session 主要解決 Cookie 不安全的問題。如果將用戶的手機號,密碼等敏感信息存放在 Cookie 中,那么風險對于雙方都是很大的。對于瀏覽器端,容易發生泄露問題。對于服務器端,因為客戶端可以隨意修改 Cookie 內容,服務端很難保證數據真實性。

那下面我們來講它是如何解決的

Session 的底層就是基于我們剛才所介紹的 Cookie 來實現的。

它是服務器端會話跟蹤技術,所以它是存儲在服務器端的。當用戶第一次訪問 Web 應用程序時,服務器會創建一個 Session,并給它分配一個唯一的標識符(session ID),然后將該標識符發送給客戶端。客戶端收到 session ID 后,通常會將其存儲在 cookie 中,以便后續請求時將其發送回服務器。服務器通過 session ID 可以找到對應的 Session,并從中讀取或修改用戶信息和狀態。session 存儲在服務器端,sessionId 會被存儲到客戶端的 cookie 中。

看到這里相比你就明白了,Session 技術無非就是請求頭中的 cookie 字段保存的是 sessionID,響應頭的 set-cookie 字段保存的也是 sessionID,而具體的用戶信息則保存在服務端。sessionID 只起到一個標識的作用。

所以 Cookie 更像是一個“身份證”,每次訪問網站時出示,表明“我是誰,我的身份證號是多少”。

而Session 更像是“指紋認證”,每次訪問網站時出示,不需要表明“我是誰…”。指紋所對于的信息全保存在服務端

Session 的流程和 Cookie 類似,就不再贅述,代碼實現如下,下面的代碼和 Cookie 很相似,但千萬不要混稀了。Session 是在服務端的,loginUser 的內容不會存放到 Cookie 中。Cookie 只存放一個 SessionID,瀏覽器端是沒有 loginUser 這個信息的。

@Slf4j
@RestController
public class SessionController {
@GetMapping("/s1")
public Result session1(HttpSession session){
log.info(“HttpSession-s1: {}”, session.hashCode());
// 往 session 中存儲數據,不是往 Cookie 中存放內容!
session.setAttribute(“loginUser”, “AirMan”);
return Result.success();
}
@GetMapping("/s2")
public Result session2(HttpServletRequest request){
HttpSession session = request.getSession();
log.info(“HttpSession-s2: {}”, session.hashCode());
// 從 session 中獲取數據,也就是從服務器中取內容
Object loginUser = session.getAttribute(“loginUser”);
log.info(“loginUser: {}”, loginUser);
return Result.success(loginUser);
}
}
為什么有了 Session 還有 Token 技術的出現呢?那是不是就說明了 Session 有一些缺點?

Session 的底層就是基于 Cookie 來實現的。Session 除了解決 Cookie 不安全的問題,其他問題都繼承過來了:

服務器集群環境下無法直接使用 Session
現在的項目基本都是集群部署,Session 保存在一個服務器中,如果這個服務器宕機了,其他服務器是沒有 SessionID 等信息的。早期解決方案是通過服務器實時同步解決,但這樣每臺服務器都要存放,開銷就很大。更好的解決方案是通過** Redis 解決共享 Session 的問題**,這里不過多討論
移動端 APP(Android、IOS) 中無法使用 Cookie
Cookie 不能跨域
那下面我們就要解決

3.Token
Token 解決了 Session 和 Cookie 的什么問題?

它解決了移動端無法使用的問題,也解決了集群環境無法使用 Cookie 的問題

那下面我們來講 Token 的實現思路

Token 是服務端生成的一串字符串,以作客戶端進行請求的一個令牌,當第一次登錄后,服務器生成一個 Token 便將此 Token 返回給客戶端,客戶端接收到令牌之后,就需要將這個令牌存儲起來。這個存儲可以存儲在 cookie 當中,也可以存儲在其他的存儲空間(比如:Authorizatrion 字段)當中。在后續的每一次請求當中,都需要將令牌攜帶到服務端。攜帶到服務端之后,接下來我們就需要來校驗令牌的有效性。如果令牌是有效的,就說明用戶已經執行了登錄操作,如果令牌是無效的,就說明用戶之前并未執行登錄操作

所以 Token 就成為了"交流"的暗號,暗號正確了,我們才能繼續交流。那么暗號的生成一定要安全。

所以 Token 的優點顯而易見

支持PC端、移動端
解決集群環境下的認證問題
減輕服務器的存儲壓力(無需在服務器端存儲)
唯一的缺點就是需要自己實現 Token 的生成(包括令牌的生成、令牌的傳遞、令牌的校驗)

Token 的類型有很多,企業開發中常使用的是 JSON Web Token(JWT),JWT 是一種開放標準(RFC 7519),用于在各方之間安全地傳輸信息。JWT 通常被用作訪問令牌,它包含了被加密的用戶信息和其他元數據,可以被用于身份驗證和授權,其本質就是一個字符串。

所以 JWT 令牌最典型的應用場景就是登錄認證

JWT 的組成: (JWT 令牌由三個部分組成,三個部分之間使用英文的點來分割)

第一部分:Header(頭), 記錄令牌類型、簽名算法等
例如:{“alg”:“HS256”,“type”:“JWT”}
第二部分:Payload(有效載荷),攜帶一些自定義信息、默認信息等
例如:{“id”:“1”,“username”:“Tom”}
第三部分:Signature(簽名),防止 Token 被篡改、確保安全性
將 header、payload,并加入指定秘鑰,通過指定簽名算法計算而來。簽名的目的就是為了防 JWT 令牌被篡改,而正是因為 JWT 令牌最后一個部分數字簽名的存在,所以整個 JWT 令牌是非常安全可靠的。一旦 JWT 令牌當中任何一個部分、任何一個字符被篡改了,整個令牌在校驗的時候都會失敗,所以它是非常安全可靠的

JWT 是如何將原始的 JSON 格式數據,轉變為字符串的呢?

在生成 JWT 令牌時,會對 JSON 格式的數據進行一次編碼:進行 base64 編碼(Base64是編碼方式,而不是加密方式)

利用 JWT 進行會話維持之后,整個流程如下

在瀏覽器發起請求來執行登錄操作,此時會訪問登錄的接口,如果登錄成功之后,我們需要生成一個jwt令牌,將生成的 jwt令牌返回給前端。
前端拿到jwt令牌之后,會將jwt令牌存儲起來。在后續的每一次請求中都會將jwt令牌攜帶到服務端。
服務端統一攔截請求之后,先來判斷一下這次請求有沒有把令牌帶過來,如果沒有帶過來,直接拒絕訪問,如果帶過來了,還要校驗一下令牌是否是有效。如果有效,就直接放行進行請求的處理。
具體的代碼實現:

首先需要引入 JWT 的依賴

io.jsonwebtokenjjwt0.9.1@Test public void genJwt(){Map

}
public static Claims parseJWT(String secretKey, String token) {
// 得到 DefaultJwtParser
Claims claims = Jwts.parser()
// 設置簽名的秘鑰
.setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
// 設置需要解析的 jwt
.parseClaimsJws(token)
.getBody();
return claims;
}
通過上述代碼即可生成一個 JWT 令牌并進行解析,需要注意的是密鑰一定盡可能的復雜

另外為了讓瀏覽器請求的時候自動攜帶 Token ,所以當登錄請求完成后,前端就會將 JWT 令牌存儲在瀏覽器本地。

服務器響應的JWT令牌存儲在本地瀏覽器哪里了呢?

在當前案例中,JWT令牌存儲在瀏覽器的本地存儲空間Local Storage中了。 Local Storage是瀏覽器的本地存儲。JWT 令牌存儲在瀏覽器的本地存儲空間Local Storage中了。Local Storage是瀏覽器的本地存儲,在移動端也是支持的

之后再發起一個查詢數據的請求,此時可以看到在請求頭中包含一個 token(JWT令牌),后續的每一次請求當中,都會將這個令牌攜帶到服務端。一般情況下,默認都是把 token 放在 Authorization 的鍵值對中。當然也可以自定義請求頭中的鍵值對,如下

最終提供給大家一個 JWT 的工具類

public class JwtUtil {
/**
* 生成jwt
* 使用Hs256算法, 私匙使用固定秘鑰
*
* @param secretKey jwt秘鑰
* @param ttlMillis jwt過期時間(毫秒)
* @param claims 設置的信息
* @return
*/
public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {
// 指定簽名的時候使用的簽名算法,也就是header那部分
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

    // 生成JWT的時間long expMillis = System.currentTimeMillis() + ttlMillis;Date exp = new Date(expMillis);// 設置jwt的bodyString jwtStr = Jwts.builder()// 如果有私有聲明,一定要先設置這個自己創建的私有的聲明,這個是給builder的claim賦值,一旦寫在標準的聲明賦值之后,就是覆蓋了那些標準的聲明的.setClaims(claims)// 設置簽名使用的簽名算法和簽名使用的秘鑰.signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))// 設置過期時間.setExpiration(exp).compact();return jwtStr;
}/*** Token解密** @param secretKey jwt秘鑰 此秘鑰一定要保留好在服務端, 不能暴露出去, 否則sign就可以被偽造, 如果對接多個客戶端建議改造成多個* @param token     加密后的token* @return*/
public static Claims parseJWT(String secretKey, String token) {// 得到DefaultJwtParserClaims claims = Jwts.parser()// 設置簽名的秘鑰.setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))// 設置需要解析的jwt.parseClaimsJws(token).getBody();return claims;
}

}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/93239.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/93239.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/93239.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

智譜AI GLM大模型 GLM-4-Plus的快速使用 ChatOpenAI類來調用GLM-4模型

智譜AIGLM-4&#xff0c;2024年1月16日發布的第四代基座大模型&#xff0c;其整體性能相較前代提升近60%&#xff0c;多維度指標逼近OpenAI的GPT-4水平。該模型支持128K上下文窗口&#xff08;約300頁文本處理能力&#xff09;&#xff0c;在長文本信息處理中實現100%精度召回。…

AsyncLocal淺復制的問題解決方案

針對C#中AsyncLocal<T>淺復制問題&#xff0c;以下是幾種主要的解決方案&#xff1a; 1. 使用不可變對象&#xff08;推薦&#xff09; 將存儲在AsyncLocal<T>中的對象設計為不可變的&#xff0c;避免修改共享狀態&#xff1a; public class ImmutableUserContext …

IIS發布.NET9 API 常見報錯匯總

記錄工作過程中發現的IIS常見錯誤。 1. HTTP Error 500.19 - Internal Server Error .NET 9 API --》vs打包方式如下&#xff1a; 發布到IIS后報錯HTTP Error 500.19 - Internal Server Error。 解決方案&#xff1a; 下載ASP.NET Core Hosting Bundle&#xff08;ASP.NET Co…

Google Chrome V8< 13.7.120 沙箱繞過漏洞

【嚴重】Google Chrome V8< 13.7.120 沙箱繞過漏洞 漏洞描述 V8 是 Google 開發的一款開源高性能 JavaScript 和 WebAssembly 引擎&#xff0c;廣泛用于 Chrome 瀏覽器和 Node.js 等項目中。 受影響版本中&#xff0c;JsonParser::MakeString 函數在處理長度為 1 的轉義字…

基于Spring Boot和Vue電腦維修平臺整合系統的設計與實現

用戶&#xff1a;注冊&#xff0c;登錄&#xff0c;在線報修&#xff0c;維修接單&#xff0c;維修報告&#xff0c;維修評價&#xff0c;個人資料維修工&#xff1a;登錄&#xff0c;在線報修&#xff0c;維修接單&#xff0c;維修報告&#xff0c;維修評價&#xff0c;通知公…

InsightFace(RetinaFace + ArcFace)人臉識別項目(預訓練模型,魯棒性很好)

背景介紹 這是一個 簡單的人臉識別項目&#xff0c;用 FastApi 在本地實現&#xff0c;使用預訓練模型&#xff0c;直接可用。 新方案比之前的FaceNet強太多了&#xff0c;甚至不用數據增強等操作&#xff0c;就可以識別戴眼鏡、不戴眼鏡、歪著的人臉等。 充分證明了選型的重要…

App Inventor 2 使用 MaterialIcons 圖標字體,快捷展示專業圖標

平時布局的話&#xff0c;如果要使用圖標&#xff0c;一般需要去找 png 圖片&#xff0c;且透明背景的。如果需要根據不同常見圖標進行變色的話&#xff0c;就需要準備多張不同樣式的圖標&#xff0c;還要考慮圖片的分辨率等等因素&#xff0c;非常的麻煩。 這時&#xff0c;如…

C語言——關于指針(逐漸清晰版)

為了更好地理解本篇文章的知識內容&#xff0c;讀者可以將以下文章作為補充知識進行閱讀 &#xff1a; C語言————原碼 補碼 反碼 &#xff08;超絕詳細解釋&#xff09;-CSDN博客 C語言————二、八、十、十六進制的相互轉換-CSDN博客 C語言————斐波那契數列的理解…

SVG 在線編輯器

SVG 在線編輯器 引言 隨著互聯網技術的發展&#xff0c;矢量圖形在網頁設計和數據可視化中扮演著越來越重要的角色。SVG&#xff08;可縮放矢量圖形&#xff09;因其文件小、無限縮放不模糊的特性&#xff0c;成為了網頁設計中常用的圖形格式。SVG 在線編輯器的出現&#xff0c…

libpostproc 已經從 ffmpeg 中移除,導致編譯 ffmpeg 時沒有 libpostproc

今天編譯 ffmpeg 時突然發現 libpostproc 不見了&#xff0c;-enable-postproc 也變成了非法的選項。用搜索引擎搜索相關信息找不到一點&#xff0c;于是去 github 看。 從提交記錄可以看到 libpostproc 已經被移除了 鏈接 主線中已經看不到了 libpostproc 這個目錄了

基于 Dell PowerEdge T440 搭建的 Proxmox VE 配置 RTX 3060 顯卡直通虛擬機、切換直通

基于 Dell PowerEdge T440 搭建的 Proxmox VE 配置 RTX 3060 顯卡直通虛擬機、切換直通 文章目錄 基于 Dell PowerEdge T440 搭建的 Proxmox VE 配置 RTX 3060 顯卡直通虛擬機、切換直通 1. 前言 2. 前提條件 3. 配置步驟 3.1. 啟用 VT-d 3.2. 激活 IOMMU 3.3. 添加 VFIO 模塊 …

如何解決pip安裝報錯ModuleNotFoundError: No module named ‘voila’問題

【Python系列Bug修復PyCharm控制臺pip install報錯】如何解決pip安裝報錯ModuleNotFoundError: No module named ‘voila’問題 摘要 在開發過程中&#xff0c;我們常常會遇到pip安裝包時出現各種錯誤&#xff0c;特別是在使用PyCharm進行開發時。本文將詳細介紹如何解決安裝…

[spring6: @EnableWebMvc]-源碼分析

源碼 EnableWebMvc EnableWebMvc 是用于啟用 Spring MVC 的注解&#xff0c;它通過導入 DelegatingWebMvcConfiguration 來加載默認的 MVC 配置&#xff0c;同時允許開發者通過實現 WebMvcConfigurer 接口來自定義部分配置&#xff1b;若需更高階的控制&#xff0c;則可直接繼承…

Jmeter的元件使用介紹:(四)前置處理器詳解

Jmeter的前置處理器可以用來在取樣器執行前做一些數據準備操作&#xff0c;也需要注意使用的作用域問題。常用的前置處理器有&#xff1a;用戶參數、BeanShell預處理器、JDBC預處理器。一、用戶參數 【用戶參數】與前面介紹過的【用戶定義的變量】有相似之處&#xff0c;先來介…

十七、K8s 可觀測性:全鏈路追蹤

十七、K8s 可觀測性&#xff1a;全鏈路追蹤 文章目錄十七、K8s 可觀測性&#xff1a;全鏈路追蹤1、Skywalking 初識1.1 為什么需要全鏈路追蹤平臺1.2 全鏈路追蹤核心組件及工作原理1.2.1 全鏈路追蹤核心概念1.2.2 全鏈路追蹤工作原理1.3 什么是Skywalking&#xff1f;1.4 Skywa…

2025 Gitee vs. GitLab:全面對比與選擇指南

在軟件研發持續加速、合規要求日益嚴格的背景下&#xff0c;選擇合適的代碼托管平臺成為團隊數字化能力建設的關鍵環節。尤其在中國本土市場&#xff0c;Gitee正憑借其深度本地化能力、全面生態整合和開源社區支撐&#xff0c;成為國內團隊首選的開發協作平臺。 一、Gitee&…

期貨反向跟單忌諱問題(一): 不斷調整盤手交易規則

在期貨反向跟單領域&#xff0c;不少運營者在摸著石頭過河的過程中&#xff0c;容易陷入一個致命誤區——對盤手交易規則的頻繁調整。這種看似“優化策略”的舉動&#xff0c;往往會讓整個跟單體系陷入惡性循環&#xff0c;最終偏離盈利初衷。期貨反向跟單的核心邏輯是&#xf…

Effective C++ 條款07:為多態基類聲明virtual析構函數

Effective C 條款07&#xff1a;為多態基類聲明virtual析構函數核心思想&#xff1a;當通過基類指針刪除派生類對象時&#xff0c;如果基類沒有虛析構函數&#xff0c;會導致派生類資源泄漏。因為此時只會調用基類的析構函數&#xff0c;而不會調用派生類的析構函數。 ?? 1. …

C++進階—C++11

第一章&#xff1a;C11簡介 在2003年C標準委員會曾經提交了一份技術勘誤表(簡稱TC1)&#xff0c;使得C03這個名字已經取代了C98稱為C11之前的最新C標準名稱。不過由于C03(TC1)主要是對C98標準中的漏洞進行修復&#xff0c;語言的核心部分則沒有改動&#xff0c;因此人們習慣性…

把振動數據轉成音頻并播放

把振動數據轉聲音并播放 1、實現流程 安裝第三方庫: pip install numpy==1.23.5 pip install scipy==1.10.1 pip install sounddevice==0.4.6流程: 1、導入振動數據 2、數據歸一化到[-1, 1]范圍 3、重采樣到44.1kHz 4、播放音頻 5、保存音頻為WAV文件(可選)2、代碼示例 …