本文学习了网络上的视频资源 此处
什么是JWT(Json Web Token)?
一种规范、思想
JSON Web令牌
官方解释
JSON Web Token (JWT) 是一个开放标准 ( RFC 7519 ),它定义了一种紧凑且自包含的方式,用于在各方之间作为 JSON 对象安全地传输信息。该信息可以被验证和信任,因为它是经过数字签名的。JWT 可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。
什么时候应该使用 ?
- 授权
- 信息交换
为什么要使用JWT认证
传统的Session认证
认证方式 我们向服务器发送请求 为了应用能识别是哪个用户发出的请求,我们只能在服务器端存储一份用户的登录信息,会再响应时传递给浏览器,让其保存为cookie,以便下次请求时发送给我们的应用。就可以识别出哪个用户了。 请求应用携带cookie找到对应的session。
传统session示例
创建一个springboot项目
创建一个controller 模拟放入session情况
/**
* @create: 2021/8/25
* @author: Tony Stark
*/
@RestController
public class TestController {
@GetMapping("/test/test")
private String test(String username, HttpServletRequest request){
//校验成功之后 把用户信息放到session中
request.getSession().setAttribute("username",username);
return "login ok!";
}
}
那么传统的session认证有什么问题呢?
每个用户认证之后,服务端都会做一次记录,通常我们的session都是保存在内存中的,认证的用户越多,服务端的压力就会增大。
我们认证之后,认证的记录被保存在内存中时,意味着下次用户要验证成功还要访问这台服务器,如果是分布式应用就限制了相应的能力
如果我们的cookie被获取了是很危险的
JWT认证简单流程
1.前端通过Web表单将自己的用户名和密码发送到后端接口 POST请求
2.后端核对用户名和密码成功后,将用户的id等其他信息作为JWT Payload负载 对其进行编码之后形成JWT(Token)
3.后端将JWT字符串作为返回值返回给前端,前端将返回的结果保存 ,退出登录时删除即可
5.前端每次请求时将JWT放入HTTP Header 头中的Authorization 授权位中
6.后端检查是否存在 , 正确性 。
7.验证通过后后端使用JWT中包含的用户信息进行操作,返回结果。
JWT结构
1.标头(header)
2.有效载荷(Payload)
3.签名(Singnature)
通常形式 header.Payload.Signature
1.header
两部分组成 : 令牌类型(JWT)和所使用的签名算法,例如HMAC、、、等 会使用Base64编码组成JWT第一部分。 默认值就是下列值 一般我们不做修改
{
"alg":"HS256",
"typ":"JWT"
}
2.payload
有效负载,其中包含声明 ,声明是用户和其他数据的声明。也会使用base64编码,官方建议不要放敏感信息,密码等等、、
{
"name": "Tom",
"admin":"root"
}
3.Singnature签名
签名需要使用前两部分base64编码后的密文+一个密钥 用header中指定的算法进行签名,用于保证JWT没被篡改过。
使用JWT
第一步 引入jwt依赖
<!--引入JWT依赖-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
获取签名
编写测试类
@Test
void testJwtCreate() {
Calendar instance = Calendar.getInstance();
instance.add(Calendar.SECOND, 5);
String token = JWT.create()
//头可以不指定一般用默认值
.withClaim("username", "张三")//payLoad
.withClaim("userId", 1)
.withExpiresAt(instance.getTime()) //指定令牌的过期时间
.sign(Algorithm.HMAC256("qweqwrpf1")); // Singnature
//Algorithm.ECDSA256()使用哪种加密算法 括号中写自己指定的密钥
System.out.println(token);
}
输出结果
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2Mjk5NDMwNDgsInVzZXJJZCI6MSwidXNlcm5hbWUiOiLlvKDkuIkifQ.tEu5AI2-XDrHy5X7xyTthAOJVPg_GaU5q2-yViDq-7c
验证签名
@Test
public void testRequire() {
//require 解密 传入加密的签名
//创建验证对象
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("qweqwrpf1")).build();
//使用验证对象中的验证方法 传入生成的JWT串 会返回一个解码的JWT
DecodedJWT decodedJWT = jwtVerifier.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2Mjk5NDcxNzQsInVzZXJJZCI6MSwidXNlcm5hbWUiOiLlvKDkuIkifQ.qZQwSXhf6omVuAOPGy1DFrOHR5NMF9nYaFIoS1qLgt4");
//获取解码之后的数据信息
System.out.println(decodedJWT.getPayload());
System.out.println("userId为 : "+decodedJWT.getClaim("userId").asInt());
System.out.println(decodedJWT.getClaims());
//获取过期时间
System.out.println("过期时间为 : "+decodedJWT.getExpiresAt());
}
输出
eyJleHAiOjE2Mjk5NDcxNzQsInVzZXJJZCI6MSwidXNlcm5hbWUiOiLlvKDkuIkifQ
userId为 : 1
{exp=com.auth0.jwt.impl.JsonNodeClaim@67080771, userId=com.auth0.jwt.impl.JsonNodeClaim@72cde7cc, username=com.auth0.jwt.impl.JsonNodeClaim@5fd4f8f5}
过期时间为 : Thu Aug 26 11:06:14 CST 2021
几种异常判断
解码会先判断算法如果算法不一致会抛出异常
com.auth0.jwt.exceptions.AlgorithmMismatchException
签名不一致
SignatureVerificationException
签名过期异常
TokenExpiredException