摘要:本文深入探讨了JWT令牌在实现会话跟踪和登录认证方面的应用,详细介绍了JWT令牌的概念、组成、生成与校验方法,以及在实际案例中如何通过JWT令牌进行会话跟踪和登录认证的具体实现步骤,为系统的安全认证机制提供了全面且深入的技术指导。
关键词:JWT令牌;会话跟踪;登录认证;生成与校验
参考资料:黑马程序员day12 完整项目请从第10天开始看
一、引言
在基于令牌技术实现会话追踪的背景下,本文着重介绍功能强大的JWT令牌,它作为用户身份标识,以字符串形式存在。接下来将详细阐述JWT令牌的相关知识及在项目中的具体应用。
二、JWT令牌
2.1 介绍
JWT,全称JSON Web Token,官网为https://jwt.io/ 。它定义了一种简洁且自包含的格式,用于在通信双方以json数据格式安全传输信息,凭借数字签名确保信息可靠。
- 简洁性:JWT表现为简单字符串,可在请求参数或请求头中直接传递。
- 自包含性:虽看似随机字符串,但可按需求在其中存储自定义数据,如用户相关信息。本质上,JWT是对原始json数据格式进行安全封装,实现通信双方的安全信息传输。
JWT令牌由三部分组成,各部分间以英文点号分隔:
- Header(头):记录令牌类型、签名算法等信息,例如:{“alg”:“HS256”,“type”:“JWT”} 。
- Payload(有效载荷):携带自定义信息、默认信息等,例如:{“id”:“1”,“username”:“Tom”} 。
- Signature(签名):用于防止Token被篡改,确保安全性。它通过将header、payload与指定秘钥结合,运用指定签名算法计算得出。签名机制使得JWT令牌极为安全可靠,一旦令牌中任何部分被篡改,校验时便会失败。
生成JWT令牌时,会对JSON格式数据进行base64编码。Base64是基于64个可打印字符表示二进制数据的编码方式,使用的字符包括A - Z、a - z、0 - 9、加号、斜杠,共64个字符,另加等号作为补位符号。需注意,Base64是编码方式,并非加密方式。
JWT令牌典型应用场景为登录认证,流程如下:
- 浏览器发起登录请求,访问登录接口,若登录成功,生成JWT令牌并返回给前端。
- 前端接收JWT令牌后存储,后续每次请求将其携带至服务端。
- 服务端统一拦截请求,判断是否携带令牌。若无令牌,拒绝访问;若有令牌,校验其有效性,有效则放行处理请求。
2.2 生成和校验
为在Java代码中生成和校验JWT令牌,首先需引入JWT依赖:
<!-- JWT依赖-->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency>
引入依赖后,可借助工具类Jwts
提供的API完成操作。
- 生成JWT代码实现:
@Test
public void genJwt(){Map<String,Object> claims = new HashMap<>();claims.put("id",1);claims.put("username","Tom");String jwt = Jwts.builder().setClaims(claims) //自定义内容(载荷) .signWith(SignatureAlgorithm.HS256, "itheima") //签名算法 .setExpiration(new Date(System.currentTimeMillis() + 24*3600*1000)) //有效期 .compact();System.out.println(jwt);
}
运行上述测试方法,输出的结果即为生成的JWT令牌。可将令牌复制至JWT官网,粘贴于Encoded位置,自动解析令牌。解析后,第一部分显示所用签名算法为HS256;第二部分为自定义数据及设置的过期时间(exp),因前两部分采用base64编码,可直接解码;第三部分由签名算法计算得出,无法直接解析。https://jwt.io/#debugger-io
校验JWT令牌(解析生成的令牌):
@Test
public void parseJwt(){Claims claims = Jwts.parser().setSigningKey("itheima")//指定签名密钥(必须保证和生成令牌时使用相同的签名密钥) .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjcyNzI5NzMwfQ.fHi0Ub8npbyt71UqLXDdLyipptLgxBUg_mSuGJtXtBk").getBody();System.out.println(claims);
}
运行该测试方法,若解析过程未报错,说明解析成功,可看到解析出的id和过期时间。若篡改令牌(如修改header中的数字),解析时会报错,表明JWT令牌被篡改。此外,修改生成令牌时设置的过期时间,过期后再解析也会报错,说明JWT令牌过期后失效。
使用JWT令牌时需注意:
- JWT校验所用签名秘钥必须与生成JWT令牌时的秘钥配套。
- 若JWT令牌解析校验报错,则表明令牌被篡改或已失效,即令牌非法。
2.3 登录下发令牌
完成JWT令牌生成和校验的基础学习后,着手在案例中运用JWT令牌技术跟踪会话,主要包含生成令牌和校验令牌两步。首先实现登录成功后生成JWT令牌并返回给前端。
查看登录接口文档的响应数据部分可知,登录成功后系统下发JWT令牌,后续请求需在请求头header中以“token”为名称,携带登录时下发的JWT令牌。若检测到用户未登录,返回固定错误信息。
实现步骤:
- 引入JWT工具类:在项目工程下创建
com.itheima.utils
包,并将提供的JWT工具类复制到该包下。 - 登录完成后,调用工具类生成JWT令牌并返回
JWT工具类
public class JwtUtils {private static String signKey = "itheima";//签名密钥private static Long expire = 43200000L; //有效时间/*** 生成JWT令牌* @param claims JWT第二部分负载 payload 中存储的内容* @return*/public static String generateJwt(Map<String, Object> claims){String jwt = Jwts.builder().addClaims(claims)//自定义信息(有效载荷).signWith(SignatureAlgorithm.HS256, signKey)//签名算法(头部).setExpiration(new Date(System.currentTimeMillis() + expire))//过期时间.compact();return jwt;}/*** 解析JWT令牌* @param jwt JWT令牌* @return JWT第二部分负载 payload 中存储的内容*/public static Claims parseJWT(String jwt){Claims claims = Jwts.parser().setSigningKey(signKey)//指定签名密钥.parseClaimsJws(jwt)//指定令牌Token.getBody();return claims;}
}
登录成功,生成JWT令牌并返回
@RestController
@Slf4j
public class LoginController {//依赖业务层对象@Autowiredprivate EmpService empService;@PostMapping("/login")public Result login(@RequestBody Emp emp) {//调用业务层:登录功能Emp loginEmp = empService.login(emp);//判断:登录用户是否存在if(loginEmp !=null ){//自定义信息Map<String , Object> claims = new HashMap<>();claims.put("id", loginEmp.getId());claims.put("username",loginEmp.getUsername());claims.put("name",loginEmp.getName());//使用JWT工具类,生成身份令牌String token = JwtUtils.generateJwt(claims);return Result.success(token);}return Result.error("用户名或密码错误");}
}
签名算法大致分类
对称加密算法
- HS256(HMAC with SHA - 256):使用 HMAC(Hash - based Message Authentication Code)算法结合 SHA - 256 哈希函数。在使用 HS256 算法时,服务器端和客户端共享一个相同的密钥(secret)。服务器使用该密钥和 HS256 算法对头部和载荷进行签名生成
JWT。客户端收到 JWT 后,使用相同的密钥和算法重新计算签名,并与 JWT 中的签名进行对比,若一致则说明 JWT 未被篡改。 - HS384(HMAC with SHA - 384):类似于 HS256,只是使用了 SHA - 384 哈希函数,提供更高的安全性,但计算成本也相对较高。
- HS512(HMAC with SHA - 512):使用 SHA - 512 哈希函数,安全性更高,但计算开销也更大,适用于对安全性要求极高的场景。
非对称加密算法
- RS256(RSA Signature with SHA - 256):基于 RSA(Rivest - Shamir - Adleman)非对称加密算法和 SHA - 256 哈希函数。服务器使用私钥对头部和载荷进行签名生成 JWT,客户端使用对应的公钥来验证签名。这种方式无需在客户端和服务器之间共享密钥,提高了安全性,常用于分布式系统或对安全性要求较高的场景。
- RS384(RSA Signature with SHA - 384):使用 RSA 算法结合 SHA - 384 哈希函数。
- RS512(RSA Signature with SHA - 512):使用 RSA 算法结合 SHA - 512 哈希函数。
椭圆曲线算法
- ES256(Elliptic Curve Signature with SHA - 256):基于椭圆曲线密码学(ECC)和 SHA - 256 哈希函数。与 RSA 相比,椭圆曲线算法在相同的安全强度下,密钥长度更短,计算速度更快,适用于对性能要求较高且对安全性有一定要求的移动设备或资源受限的环境。
- ES384(Elliptic Curve Signature with SHA - 384):使用椭圆曲线算法结合 SHA - 384 哈希函数。
- ES512(Elliptic Curve Signature with SHA - 512):使用椭圆曲线算法结合 SHA - 512 哈希函数。