优点:
HTTP
协议中支持的技术
缺点:
Cookie
跨域区分三个维度:协议、IP/域名、端口
每个域下面都有各自的 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
了
SessionID
(JSESSIONID
)Session
就解决了 Cookie 的这个问题:Cookie 是明文存储在用户本地,而且带有大量的用户信息,不太安全
Session 就是把用户的会话信息存储在服务端,然后颁发给客户端一个 sessionId,让客户端之后带着 sessionId 来请求。这样服务端就可以通过 sessionId 去找到这个用户的信息,从而识别请求。
那么客户端是如何带上 sessionId 的?
这个 sessionId 还是按照 Cookie 的形式存储在用户的本地,发起请求的时候带上即可。
// 设置 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
@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 简单地说就是一个含有凭证信息的令牌,只要服务器能识别这个令牌就能根据令牌的身份进行相应的相应。
全称: JSON Web Token
定义了一种简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的。
组成:
@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}
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();
}
/**
* 从令牌中获取用户ID
*/
public static Long getUserIdFromToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
return claims.get("id", Long.class);
}
/**
* 从令牌中获取用户账号
*/
public static String getUserAccountFromToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
return claims.get("userAccount", String.class);
}
/**
* 从令牌中获取用户名
*/
public static String getUserNameFromToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
return claims.get("userName", String.class);
}
/**
* 从令牌中获取所有声明
*/
public static Claims getClaimsFromToken(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
}
/**
* 检查令牌是否即将过期(用于刷新令牌)
*/
public static boolean isTokenExpiringSoon(String token) {
try {
Claims claims = getClaimsFromToken(token);
Date expiration = claims.getExpiration();
// 如果令牌在30分钟内过期,则认为即将过期
return expiration.before(new Date(System.currentTimeMillis() + 30 * 60 * 1000));
} catch (Exception e) {
return true;
}
}
/**
* 验证 JWT 令牌
*/
public static boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token);
return true;
} catch (SignatureException e) {
System.out.println("无效的 JWT 签名");
} catch (MalformedJwtException e) {
System.out.println("无效的 JWT 令牌");
} catch (ExpiredJwtException e) {
System.out.println("JWT 令牌已过期");
} catch (UnsupportedJwtException e) {
System.out.println("不支持的 JWT 令牌");
} catch (IllegalArgumentException e) {
System.out.println("JWT 令牌为空");
}
return false;
}
需要先获取用户登录信息,然后才能根据用户信息生成令牌
@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"
}
end
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。