會話跟蹤方案
Cookie
什么是Cookie?
- 概念:存儲在用戶瀏覽器端的一個小型數據文件,用于跟蹤和保存用戶的狀態信息
- 用處:主要用于保持用戶登錄狀態、跟蹤用戶行為、存儲用戶偏好等
- 存儲在瀏覽器端
優點:
HTTP協議中支持的技術
缺點:
- 移動端無法使用
- 不安全,用戶可以自己禁用
Cookie - Cookie 不能跨域
跨域區分三個維度:協議、IP/域名、端口
關于 Cookie

每個域下面都有各自的 Cookie,訪問不同的網站帶屬于這個網站的Cookie,不會帶別人的 Cookie,否則就會亂套了,前后端也是一樣,瀏覽器會發送請求并且該請求攜帶Cookie到前端,而這個Cookie如果拿去訪問后端就不可行,因為Cookie不能跨域
問題:但是 Cookie 是明文存儲在用戶本地,而且帶有大量的用戶信息,這不太安全
相關代碼實踐
// 設置 Cookie
@GetMapping("/c1")
public BaseResponse cookie1(HttpServletResponse response) {
response.addCookie(new Cookie("loginUser", "lantz"));
return ResultUtils.success("ok");
}
// 獲取 Cookie
@GetMapping("/c2")
public BaseResponse cookie2(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
if (cookie.getName().equals("loginUser")) {
log.info("loginUser value=" + cookie.getValue());
}
}
return ResultUtils.success("ok");
}
在測試c2接口的時候可以看到debug窗口會返回一條數據:
2025-10-12 18:23:29.206 INFO 37868 --- [nio-8080-exec-5] c.l.l.controller.SessionController : loginUser value=lantz
說明已經成功獲取Cookie了
Session
什么是 Session?
- 概念:服務器端保存用戶狀態的機制,每個用戶會話都有一個唯一的
SessionID(JSESSIONID) - 用處:主要用于跟蹤用戶在服務器上的狀態信息,例如登錄狀態和購物車內容
- 存儲在服務器端,然后對應的 Session ID 通過 Cookie 保存在客戶端瀏覽器中

關于 Session
Session就解決了 Cookie 的這個問題:Cookie 是明文存儲在用戶本地,而且帶有大量的用戶信息,不太安全
Session 就是把用戶的會話信息存儲在服務端,然后頒發給客戶端一個 sessionId,讓客戶端之后帶著 sessionId 來請求。這樣服務端就可以通過 sessionId 去找到這個用戶的信息,從而識別請求。
那么客戶端是如何帶上 sessionId 的?
這個 sessionId 還是按照 Cookie 的形式存儲在用戶的本地,發起請求的時候帶上即可。
演示代碼
設置Session
// 設置 Session
@GetMapping("/s1")
public BaseResponse session1(HttpSession session) {
log.info("HttpSession-s1: {}", session.hashCode());
session.setAttribute("loginUser", "lantz");
return ResultUtils.success("ok");
}
測試s1設置session并且輸出當前Session的哈希碼
如下所示:
HttpSession-s1: 2054650805
返回的Cookie

可見:在發送Session請求的時候,瀏覽器會為當前的請求自動生成一個哈希碼,用作JSESSIONID,并且會將這個JSESSIONID加到Cookie上,一并返回給服務端
獲取Session
// 獲取 Session
@GetMapping("/s2")
public BaseResponse session2(HttpServletRequest request) {
HttpSession session = request.getSession();
log.info("HttpSession-s2: {}", session.hashCode());
Object loginUser = session.getAttribute("loginUser");
log.info("loginUser: {}", loginUser);
return ResultUtils.success("ok");
}
測試s2獲取到當前Session的哈希碼
如下所示:
2025-10-12 18:40:43.562 INFO 16300 --- [nio-8080-exec-4] c.l.l.controller.SessionController : HttpSession-s2: 2054650805
2025-10-12 18:40:43.562 INFO 16300 --- [nio-8080-exec-4] c.l.l.controller.SessionController : loginUser: lantz
Cookie 展示:

Token
什么是Token?
- 概念:本質是一個加密的字符串,用于身份驗證和授權,可以包含用戶信息和權限,用于驗證用戶身份或授權訪問資源。
- 認證后,后端服務會返回 Token,存儲在客戶端(瀏覽器或移動應用中),后續客戶端訪問服務端需要帶上這個 Token
其實只需要一個能代表身份的憑證即可,一個服務端頒發給用戶的憑證,之后的請求讓用戶帶著這個憑證就行。
就像我們的身份證代表著我們,里面包含了我們的信息
但是擔心憑證被偽造怎么辦?可以把憑證給簽名了,這樣服務器就可驗證憑證的真偽了。
這個憑證就叫 **Token**
如果一個用戶登陸了系統,就返回一個 Token 給他,之后每次請求他帶這個 Token 來就行。服務器驗證了真偽之后拿到 Token 里面的用戶信息就知道這個請求是誰發的了。
Token 簡單地說就是一個含有憑證信息的令牌,只要服務器能識別這個令牌就能根據令牌的身份進行相應的相應。
jwt
全稱: JSON Web Token(https://jwt.io/)
定義了一種簡潔的、自包含的格式,用于在通信雙方以json數據格式安全的傳輸信息。由于數字簽名的存在,這些信息是可靠的。
組成:
- 第一部分:Header(頭),記錄令牌類型、簽名算法等。例如:
- 第二部分:Payload(有效載荷),攜帶一些自定義信息、默認信息等。例如:
- 第三部分:Signature(簽名),防止Token被篡改、確保安全性。將header、payload,并加入指定秘鑰,通過指定簽名算法計算而來。
代碼演示
生成令牌
@Test
public void getjwt(){
Map<String, Object> claims = new HashMap<>();
claims.put("id", 123);
claims.put("username", "lantz");
String jwt = Jwts.builder()
.setClaims(claims) // 自定義載荷
.signWith(SignatureAlgorithm.HS256, "lanuc") // 簽名算法
.setExpiration(new Date(System.currentTimeMillis() + 3600 * 100)) // 設置過期時間
.compact();
System.out.println(jwt);
}
生成令牌如下:
eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MTIzLCJleHAiOjE3NjAyNjc5NDQsInVzZXJuYW1lIjoibGFudHoifQ.9RYwpzNji9kfg33ge1qQUDFq_2vQdO__mr1Eze-IXlc
解析令牌
@Test
public void deCode(){
Claims body = Jwts.parser()
.setSigningKey("lanuc") // 指定簽名秘鑰
.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MTIzLCJleHAiOjE3NjAyNjc5NDQsInVzZXJuYW1lIjoibGFudHoifQ.9RYwpzNji9kfg33ge1qQUDFq_2vQdO__mr1Eze-IXlc")//解析令牌
.getBody();
System.out.println(body);
}
生成結果:
{id=123, exp=1760267944, username=lantz}
SpringBoot 實現
JwtUtils
jwt工具類(JwtUtils)-- 生成令牌,解析令牌
生成令牌
/**
* 生成令牌
* @param claims JWT 第二部分負載 payload 中存儲的內容
* @return
*/
public static String generateToken(Map<String, Object> claims) {
return Jwts.builder()
.setClaims(claims)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
解析令牌
/**
* 解析JWT Token
* @param token jwt 令牌
* @return
*/
public static Claims parseToken(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
}
用戶登錄
需要先獲取用戶登錄信息,然后才能根據用戶信息生成令牌
@PostMapping("/login")
public BaseResponse<LoginResponse> userLogin(@RequestBody UseLoginRequest useLoginRequest, HttpServletRequest request){
if (useLoginRequest == null){
return null;
}
String userAccount = useLoginRequest.getUserAccount();
String userPassword = useLoginRequest.getUserPassword();
if (StringUtils.isAnyBlank(userAccount, userPassword)) {
return null;
}
User user = userService.userLogin(userAccount, userPassword, request);
if (user == null) {
return ResultUtils.error(ErrorCode.PARAMS_ERROR, "登錄失敗");
}
// 獲取登錄用戶
User loginUer = userService.getLoginUer(request);
// 生成令牌,下發令牌
Map<String, Object> claims = new HashMap<>();
claims.put("id", loginUer.getId());
claims.put("userAccount", loginUer.getUserAccount());
claims.put("userName", loginUer.getUserName());
String jwt = JwtUtils.generateToken(claims); // 生成令牌
// 創建登錄響應
LoginResponse loginResponse = new LoginResponse();
loginResponse.setUser(user);
loginResponse.setJwt(jwt);
return ResultUtils.success(loginResponse);
}
測試結果:
{
"code": 0,
"data": {
"user": {
"id": 4,
"userAccount": "Lantz",
"userName": "lan",
"userAvatar": "https://pic.code-nav.cn/user_avatar/1872161376428277761/thumbnail/D5QaPIaHNcft2xW1.jpg",
"gender": 1,
"userPassword": "dd8fa48dc3e51a450c6141f5c0ad6665",
"phone": "12335555",
"email": "22334466@email.com",
"userStatus": 0,
"createTime": "2025-04-18T10:43:38.000+00:00",
"updateTime": null,
"isDelete": null,
"userRole": 1
},
"jwt": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyQWNjb3VudCI6IkxhbnR6IiwiaWQiOjQsInVzZXJOYW1lIjoibGFuIiwiZXhwIjoxNzYwMjczMzMxfQ.eWcD38s2pfmYin78IssVy2tqeq0SA5CWtAjbq5CKto0"
},
"message": "ok"
}

浙公網安備 33010602011771號