前言
在现代Web开发中,身份认证是一个永恒的话题。随着前后端分离架构的流行,JWT(JSON Web Token)逐渐成为身份认证的主流方案。本文将通过图文方式,深入解析JWT的原理,并与传统的Cookie、Session、Token进行全面对比。
一、JWT是什么?
JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在网络应用间安全传输信息。它是一个紧凑、自包含的JSON对象,主要用于身份认证和信息交换。
1.1 JWT的三大特点
-
无状态:服务器不需要保存会话信息
-
自包含:包含了所有必要的信息
-
可扩展:支持分布式系统
1.2 JWT的优缺点分析
✅ 优点:
-
无状态:服务器不需要保存会话信息,易于扩展
-
跨域支持:支持CORS,适合前后端分离
-
自包含:包含了所有必要信息,减少数据库查询
-
标准化:行业标准,支持多种语言
❌ 缺点:
-
无法强制失效:一旦签发,到期前始终有效
-
载荷大小有限:不宜存储过多信息
-
安全性依赖实现:需要正确配置签名算法
-
不适合存储敏感信息:Payload只是Base64编码
二、JWT的结构详解
JWT由三部分组成,用点(.)分隔:
JWT结构:Header.Payload.Signature

2.1 Header(头部)
包含令牌的类型和使用的签名算法:
javascript
{
"alg": "HS256", ---算法为HS256
"typ": "JWT" ---类型为jwt
}
2.2 Payload(载荷)
包含声明信息(用户数据),想写几个写几个,该内容会被解码,建议不要使用敏感信息:
javascript
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"exp": 1516239022
}
2.3 Signature(签名)
用于验证消息的完整性和真实性,防止串改payload中的数据,且该部分无法被解码:
组成为:header加密+payload加密+秘钥
javascript
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
三、JWT工作流程

四、JWT 、Cookie 、Session 、Token 对比
4.1 概念对比表

4.2 安全性对比

4.3 实际适合场景推荐

五、springboot实践案例
5.1 在pom.xml加依赖
XML
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.6.0</version>
</dependency>
若jdk版本在1.8以上的话,还需要加上下面的依赖:
XML
<dependency>
<groupId>com.sun.bind</groupId>
<artifactId>jaxb.api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.bind</groupId>
<artifactId>jaxb.impl</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
5.2 创建工具类JwtUtil
java
public class JwtUtil {
// private static long tokenExpiration = 1000 * 60 * 60 * 24;
private static long tokenExpiration = 1000 * 20;
private static String tokenSignKey = "a1d23mi789n";
public static String createToken(String id,String mobile){
String token = Jwts.builder()
//载荷:自定义信息
.claim("id", id)
.claim("mobile", mobile)
//载荷:默认信息
.setSubject("uushop-user") //令牌主题
.setExpiration(new Date(System.currentTimeMillis()+tokenExpiration)) //过期时间
.setId(UUID.randomUUID().toString())
//签名哈希
.signWith(SignatureAlgorithm.HS256, tokenSignKey)
//组装jwt字符串
.compact();
return token;
}
public static boolean checkToken(String token){
if(StringUtils.isEmpty(token)){
return false;
}
try {
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
} catch (Exception e) {
return false;
}
return true;
}
}
5.3 创建实体类
java
@Data //若要使用该注解需要添加lombok依赖
public class User {
private String username;
private String password;
private String token;
}
5.4 创建UserController类
java
@RestController
public class UserController {
private final String USERNAME = "admin";
private final String PASSWORD = "123123";
@GetMapping("/login")
public User login(User user){
if(USERNAME.equals(user.getUsername())&&PASSWORD.equals(user.getPassword())){
//添加token
user.setToken(JWTUtil.createToken());
return user;
}
return null;
}
//将token值传给前端,前端的点击登录事件中,通过
localStorage.setItem("token",JSON.stringify(response.data))将token值进行保存,并进行路由的
跳转,
// 在登录时如果该用户不存在,则返回登录界面,
// 如果用户存在则前端即将进入的页面里面通过以下代码:
// created(){window.localStorage.getItem("token")}将token值取出来通过将token值然后传给
后端
//同时,将token存在前端的路由index.js文件中的axios中的header中的token中
//后端将前端传过来的token值进行校验
@GetMapping("/checkToken")
public Boolean checkToken(HttpServletRequest request){
String token = request.getHeader("token");
return JWTUtil.checkToken(token);
}
}
一般工作中都是要结合redis使用的。大家可以探索一下。
💬 欢迎留言讨论: 你在项目中是如何选择认证方案的?遇到过哪些JWT相关的问题?欢迎在评论区分享你的经验!