这是我参与「第四届青训营 」笔记创作活动的的第十四天
写在前面
在写大项目时,作为前后端分离的形式开发,为判断用户身份、用户登录状态等,因此我进行了JWT的学习
基本概念
http是一个无状态的响应,如何判断是哪个用户发出的请求成为一个问题
- 传统session
- 原理:服务器与客户端第一次响应成功后,服务器会创建并保存一个session对象,同时给客户端返回的cookie中加入sessionId(cookie里为JSESSIONID),同理客户端再次访问时也会带着在cookie中加入sessionId,服务端根据sessionId找到对应的session对象
- 问题
- session对象保存在服务端,对服务端的开销压力大
- 若验证记录保存在一台服务器的内存上,则意味着用户的下次请求还得在这台服务器上,限制了分布式开发中的负载均衡能力,限制了用于的拓展能力
- 若cookie被拦截、篡改,容易造成伪造攻击
- 不利于前后端分离
- sessionId是一个特征值,表达的信息不够丰富
- 后端多节点部署还得考虑sessionId共享
- JWT
- JWT原理示意图
- 优势
- 数据量小,传输速度快
- 自包含用户的一些信息,减少数据库的访问次数
- token以json加密的形式保存在客户端,因此JWT是跨语言的,原则上所有web形式都支持
- 不需要在服务端保存会话信息,利于分布式微服务
- JWT原理示意图
JWT组成
- 标头(Header)
- 明文
{ "alg" : 'HS256',//签名算法 "typ" : 'JWT'//令牌类型 } - 密文
- 进行Base64编码,得到JWT的第一部分,日后可解码
- 明文
- 有效负载(payload)
- 包含有关实体和其它数据的声明,同样会进行Base64编码组成JWT的第二部分
- 明文
{ "sub" : '123456',//用户id "name" : '用户名', "admin" : true//角色 }
- 签名(signature)
- 由使用编码后的header和payload以及我们提供的密钥(不能泄露),根据header中指定的签名算法进行签名,若得到的结果与signature的值一样则标识该令牌是服务端认可的令牌,同时可以保证JWT没有被篡改
JWT使用
- 引入依赖
<!-- JWT --> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.0</version> </dependency> - 令牌获取
HashMap<String, Object> map = new HashMap<>(); Calendar instance = Calendar.getInstance(); instance.add(Calendar.SECOND,20); String token = JWT.create().withHeader(map)//头部,默认可不写 .withClaim("key", value)//payload .withExpiresAt(instance.getTime())//过期时间 .sign(Algorithm.HMAC256("密匙"));//签名 - 验证签名
//生成验证对象 JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("密匙")).build(); //验证token DecodedJWT verify = jwtVerifier.verify("token"); //过期时间 System.out.println(verify.getExpiresAt()); System.out.println(verify.getClaims().get("userId").asInt()); System.out.println(verify.getClaims().get("username").asString()); - 常见异常
| 名称 | 含义 | | --- | --- | | SignatureVerificationException | 签名不一致异常 | | 签名不一致异常 | 令牌过期异常 | | AlgorithmMismatchException | 算法不匹配异常 | | InvalidClaimException | 失效的payload异常 |
写在最后
以上便是我的一些学习笔记,若有不足,欢迎指出