• JWT 使用入门(一)配置与示例


    官网链接:https://github.com/jwtk/jjwt

    1、用途✨

    JWT主要作用分为以下两种:

    • 1:作为在前后端分离项目中的登录策略
    • 2:单纯作为客户端的请求身份认证,spring-gateway网关进行判断拦截

    这里我仅讲解第二种!第一种见链接:

    2、实现流程👀

    • 1:服务方编写代码生成token,记录token给消费方
    • 2:消费方每次发来的请求,都要携带该token
    • 3:服务方部署网关对消费方发送来的token进行解密,判断是否有效:有效放行到服务端,否则进行拦截

    注:这种不需要存储在服务端的认证方式时无状态认证方式,不同于session+cookie的有状态认证方式,其中好处主要为以下几点:

    • 客户端请求不依赖服务端的信息,多次请求不需要必须访问到同一台服务器
    • 减小服务端存储压力

    3、JWT介绍

    JWT,全称是Json Web Token, 是一种JSON风格的轻量级的授权和身份认证规范,可实现无状态、分布式的Web应用授权!

    3.1、构成

    JWT是由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串。第一部分我们称它为头部(header),第二部分我们称其为载荷(payload),第三部分是签证(signature).

    3.1.1、header

    JWT头部分是一个描述JWT元数据的JSON对象,通常如下所示:

    {
      "alg": "HS256",
    }
    
    • 1
    • 2
    • 3

    在这里插入图片描述
    有的文章说还包含:typ属性表示令牌的类型,JWT令牌统一写为JWT,但是我打印的如上图只有alg。

    3.1.2、载荷

    一共有7个字段可以设置,如下:

    iss: jwt签发者
    sub: jwt所面向,使用jwt的用户
    aud: 接收jwt的一方
    exp: jwt的过期时间,这个过期时间必须大于签发时间
    nbf: 定义在指定时间之前,该jwt都是不可用的.
    iat: jwt的签发时间
    jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    另外,可以自定义一些字段,如下:

    {
      "admin": true
    }
    
    • 1
    • 2
    • 3

    3.1.3、签证

    生成语法:

    HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
    
    • 1

    注:其中secret是密钥,不可泄漏,并且其生成也用到了头部信息和载荷信息。

    JWT解析地址:https://jwt.io/#libraries

    4、java实现

    4.1、引入依赖

    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.0</version>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4.2、测试案例

    生成代码:

    package org.example;
    
    import io.jsonwebtoken.*;
    import io.jsonwebtoken.impl.Base64Codec;
    
    import java.util.Date;
    
    // Press Shift twice to open the Search Everywhere dialog and type `show whitespaces`,
    // then press Enter. You can now see whitespace characters in your code.
    public class Main {
        public static void main(String[] args) {
            // Press Alt+Enter with your caret at the highlighted text to see how
            // IntelliJ IDEA suggests fixing it.
    
            //testJwt();
            generateJwtToken();
            testParseToken();
    
        }
    
        /** 测试生成token1 */
        public static void testJwt() {
            JwtBuilder jwtBuilder = Jwts.builder()
                    // 唯一ID {"":""}
                    .setId("888")
                    // 接受的用户 {"sub":"Rose"}
                    .setSubject("Rose")
                    // 签发时间 {"iat":"。。。"}
                    .setIssuedAt(new Date())
                    // 签名算法 及秘钥
                    .signWith(SignatureAlgorithm.HS256, "aVBhbkUxcEEyc1dvMmRCeVpM");
            // 签发token
            String token = jwtBuilder.compact();
            System.out.println(token);
    
            String[] split = token.split("\\.");
            // 头部
            System.out.println(Base64Codec.BASE64.decodeToString(split[0]));
            // 载荷
            System.out.println(Base64Codec.BASE64.decodeToString(split[1]));
            // 算法及秘钥 这个会乱码
            System.out.println(Base64Codec.BASE64.decodeToString(split[2]));
            
        }
    	 /** 测试生成token2 */
        public static String generateJwtToken(){
     
            // 头部 map / Jwt的头部承载,第一部分
            // 可不设置 默认格式是{"alg":"HS256"}
            Map<String, Object> map = new HashMap<>();
                map.put("alg", "HS256");
                map.put("typ", "JWT");
     
     
            //载荷 map / Jwt的载荷,第二部分
            Map<String,Object> claims = new HashMap<String,Object>();
     
                //私有声明 / 自定义数据,根据业务需要添加
                claims.put("id","123456");
                claims.put("userName", "admin");
     
                //标准中注册的声明 (建议但不强制使用)
                //一旦写标准声明赋值之后,就会覆盖了那些标准的声明
                claims.put("iss", jwt_iss);
                /*	iss: jwt签发者
                    sub: jwt所面向的用户
                    aud: 接收jwt的一方
                    exp: jwt的过期时间,这个过期时间必须要大于签发时间
                    nbf: 定义在什么时间之前,该jwt都是不可用的.
                    iat: jwt的签发时间
                    jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
                */
     
     
            //下面就是在为payload添加各种标准声明和私有声明了
            return Jwts.builder() // 这里其实就是new一个JwtBuilder,设置jwt的body
                    .setHeader(map)         // 头部信息
                    .setClaims(claims)      // 载荷信息
                    .setId(UUID.randomUUID().toString()) // 设置jti(JWT ID):是JWT的唯一标识,从而回避重放攻击。
                    .setIssuedAt(new Date())       // 设置iat: jwt的签发时间
                    .setExpiration(new Date(System.currentTimeMillis() + access_token_expiration * 1000)) // 设置exp:jwt过期时间
                    .setSubject(subject)    //设置sub:代表这个jwt所面向的用户,所有人
                    .signWith(SignatureAlgorithm.HS256, "aVBhbkUxcEEyc1dvMmRCeVpM");//设置签名:通过签名算法和秘钥生成签名
                    .compact(); // 开始压缩为xxxxx.yyyyy.zzzzz 格式的jwt token
        }
    
        /** 解析token */
        public static void testParseToken() {
            String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiJSb3NlIiwiaWF0IjoxNjkwOTY1ODY3fQ.pT-fzrxquCmB5eLqIiPQfdow92c0ZJBHP79-W3KE898";
             //得到的是荷载
            Claims claims = (Claims) Jwts.parser()
                    // 解析时的秘钥一定要和签发时秘钥相同,但是发现这了的秘钥为4-7个x都是可以成功解析的
                    .setSigningKey("aVBhbkUxcEEyc1dvMmRCeVpM111")
                    // .parse(token)
                    .parseClaimsJws(token)
                    .getBody();
            System.out.println(claims);
            // 得到的是头部
            JwsHeader test = Jwts.parser()
                    // 解析时的秘钥一定要和签发时秘钥相同,但是发现这了的秘钥为4-7个x都是可以成功解析的
                    .setSigningKey("aVBhbkUxcEEyc1dvMmRCeVpM111")
                    // .parse(token)
                    .parseClaimsJws(token)
                    .getHeader();
            System.out.println(test);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107

    参考链接:
    https://blog.csdn.net/qq_37138756/article/details/103499429

  • 相关阅读:
    深度解析:Web 3.0和元宇宙
    31年前的Beyond演唱会,是如何超清修复的?
    Antd的table组件自定义筛选菜单的使用
    桌面云架构讲解(VDI、IDV、VOI/TCI、RDS)
    【LeetCode】每日一题 -- 1170. 比较字符串最小字母出现频次 -- Java Version
    【vue设计与实现】非原始值的响应式方案 10-如何代理Set和Map
    【leetcode】【2022/8/23】782. 变为棋盘
    C#:出题并判断
    unity ugui text 超链接和下划线,支持部分富文本格式
    Java学习【深入解读File类:从基础到高级的完整指南】
  • 原文地址:https://blog.csdn.net/qq_37534947/article/details/132066909