JWT学习记录| 青训营笔记

94 阅读2分钟

这是我参与「第四届青训营 」笔记创作活动的的第十四天

写在前面

在写大项目时,作为前后端分离的形式开发,为判断用户身份、用户登录状态等,因此我进行了JWT的学习


基本概念

http是一个无状态的响应,如何判断是哪个用户发出的请求成为一个问题

  • 传统session
    • 原理:服务器与客户端第一次响应成功后,服务器会创建并保存一个session对象,同时给客户端返回的cookie中加入sessionId(cookie里为JSESSIONID),同理客户端再次访问时也会带着在cookie中加入sessionId,服务端根据sessionId找到对应的session对象
    • 问题
      • session对象保存在服务端,对服务端的开销压力大
      • 若验证记录保存在一台服务器的内存上,则意味着用户的下次请求还得在这台服务器上,限制了分布式开发中的负载均衡能力,限制了用于的拓展能力
      • 若cookie被拦截、篡改,容易造成伪造攻击
      • 不利于前后端分离
        • sessionId是一个特征值,表达的信息不够丰富
        • 后端多节点部署还得考虑sessionId共享
  • JWT
    • JWT原理示意图 image.png
    • 优势
      • 数据量小,传输速度快
      • 自包含用户的一些信息,减少数据库的访问次数
      • token以json加密的形式保存在客户端,因此JWT是跨语言的,原则上所有web形式都支持
      • 不需要在服务端保存会话信息,利于分布式微服务

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异常 |

写在最后

以上便是我的一些学习笔记,若有不足,欢迎指出