美文网首页SpringCloud
JWT的基本使用

JWT的基本使用

作者: 水煮鱼又失败了 | 来源:发表于2021-03-06 21:19 被阅读0次

    1 场景

    JSON Web Token (JWT)是一种开放标准(RFC 7519),它定义了一种紧凑和自包含的方式,用于作为JSON对象在各方之间安全地传输信息。这个信息可以被验证和信任,因为它是数字签名的。JWTs可以使用密钥(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥进行签名。

    官网:https://jwt.io/

    2 说明

    2.1 结构

    在其紧凑的形式中,JSON Web令牌由点(.)分隔的三个部分组成,它们是:

    • Header
    • Payload
    • Signature

    因此,JWT通常如下所示:

    xxxxx.yyyyy.zzzzz
    

    即,如下格式:

    Header.Payload.Signature
    

    2.2 组成

    2.2.1 Header

    Header通常由两部分组成:令牌的类型,即JWT,以及所使用的签名算法,如HMAC SHA256或RSA。

    如下:

    {
      "alg": "HS256",
      "typ": "JWT"
    }
    

    然后,该JSON是Base64Url编码的,以形成JWT的第一部分。

    2.2.2 Payload

    令牌的第二部分是Payload,它包含声明claims。声明是关于实体(通常是用户)和附加数据的声明。声明claims有三种类型:registered、public、和private

    (1)registered

    registered类型的claims。
    这些是一组预定义claims,它们不是强制性的,而是推荐的,以提供一组有用的、可互操作的claims。其中包括:iss(发行人)exp(到期时间)sub(主题)aud(目标受众)等。

    注意,声明名claims只有三个字符长,因为JWT是为了紧凑。
    如:iss、exp、sub、aud

    (2)public

    这些可以由使用JWTs的人随意定义。但是为了避免冲突,应该在 IANA JSON Web Token Registry 令牌注册表中定义它们,或者将它们定义为包含抗冲突名称空间的URI。

    IANA JSON Web Token Registry 中定义的claimsName信息如下,定义的为默认定义的claimName的含义:

    1615019699644.png

    即如果定义的Claim Name,为避免冲突,需参照IANA JSON Web Token Registry中定义的ClaimName,如需`定义其他ClaimName,需不要和这里定义的ClaimName冲突。

    可以在此部分配置定义的要传递的业务信息,如:用户信息、部门信息、角色信息等。

    (3)private

    这些自定义claims是为了在同意使用它们的各方之间共享信息而创建的,它们既不是registered,也不是public的claims。

    一个有效的Payload的例子如下:

    {
      "sub": "1234567890",
      "name": "John Doe",
      "admin": true
    }
    

    然后对claims进行Base64Url编码,以形成JSON Web令牌的第二部分

    请注意,对于已签名的令牌,该信息虽然受到保护,不会被篡改,但任何人都可以读懂。不要将机密信息放在JWT的有效负载或头元素中除非它被加密了

    2.2.3 Signature

    要创建签名Signature部分,必须获取已编码的Header已编码的Payloadhead中指定的密钥head中指定的算法
    根据获取的上述信息,生成签名Signature。

    例如,使用HMAC SHA256算法时,签名的生成方式如下:

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

    即格式如下:

    head算法xxx(base64UrlEncode(json格式的header) + "." +base64UrlEncode(json格式的header),head密钥xxx)
    

    签名用于验证消息在整个过程中没有被篡改,而且,在使用私钥签名的令牌的情况下,它还可以验证JWT的发送方是它所声称的那个人

    2.2.4 汇总

    输出是三个用点分隔的Base64-URL字符串,它们可以很容易地在HTML和HTTP环境中传递,同时与基于xml的标准(如SAML)相比更紧凑。

    下面展示了一个JWT,它对前面的头和有效负载进行了编码,并使用secret对其进行了签名。

    encoded-jwt3.png

    2.3 在线调试

    JWT的在线调试验证地址: jwt.io Debugger

    如下:

    1615020200659.png

    3 Java实现

    这里使用java-jwt来实现JWT的操作。

    3.1 maven依赖

    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>3.14.0</version>
    </dependency>
    

    3.2 工具类封装

    
    import com.auth0.jwt.JWT;
    import com.auth0.jwt.JWTCreator;
    import com.auth0.jwt.JWTVerifier;
    import com.auth0.jwt.algorithms.Algorithm;
    import com.auth0.jwt.interfaces.DecodedJWT;
    import com.auth0.jwt.interfaces.Verification;
    
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.concurrent.TimeUnit;
    
    /**
     * JWT工具类
     */
    public class JWTUtil {
        
        /**
         * 生成签名
         * @param claimMap          claimMap
         * @param secret            密钥
         * @param expireMilliSecond 过期时间-毫秒(如果为null,则无过期时间)
         * @return
         */
        public static String sign(Map<String, String> claimMap, String secret, Long expireMilliSecond) {
            Algorithm algorithm = Algorithm.HMAC256(secret);
            JWTCreator.Builder builder = JWT.create();
            if (claimMap != null && claimMap.size() > 0) {
                for (Map.Entry<String, String> entry : claimMap.entrySet()) {
                    String key = entry.getKey();
                    if (key == null || key.equals("")) {
                        continue;
                    }
                    builder.withClaim(key, entry.getValue());
                }
            }
            if (expireMilliSecond != null) {
                builder.withExpiresAt(new Date(System.currentTimeMillis() + expireMilliSecond));
            }
            return builder.sign(algorithm);
        }
        
        /**
         * 验证token
         * @param token  token
         * @param secret 密钥
         * @return
         */
        public static DecodedJWT verify(String token, String secret) {
            try {
                Algorithm algorithm = Algorithm.HMAC256(secret);
                Verification verification = JWT.require(algorithm);
                JWTVerifier verifier = verification.build();
                DecodedJWT jwt = verifier.verify(token);
                return jwt;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
        
        /**
         * 获取JWT中内容
         * @param jwt       jwt
         * @param claimName claim名称
         * @return
         */
        public static String getClaimValueByJwt(DecodedJWT jwt, String claimName) {
            return jwt.getClaim(claimName).asString();
        }
        
        /**
         * 获取token中内容
         * @param token     token
         * @param claimName claim名称
         * @return
         */
        public static String getClaimValueByToken(String token, String claimName) {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim(claimName).asString();
        }
    }
    
    

    3.3 使用示例

    3.3.1 代码
    public static void main(String[] args) throws Exception {
        // ====================【参数定义】====================
        // (1)密钥
        String secret = "x123456";
    
        // (2)自定义claim内容
        Map<String, String> claimMap = new HashMap<>();
        claimMap.put("name", "张三");
        claimMap.put("roleId", "1");
    
        // (3)超时时间-3小时(单位:毫秒)
        Long expireSecond = 3 * 60 * 60 * 1000L;
    
        System.out.println("====================【生成token】====================");
        String token = JWTUtil.sign(claimMap, secret, expireSecond);
        System.out.println("[生成-token]:" + token);
    
        System.out.println("\n====================【验证token,并获取自定义属性】====================");
        DecodedJWT jwt = JWTUtil.verify(token, secret);
        boolean verifyResult = jwt == null ? false : true;
        System.out.println("[token验证结果]:" + verifyResult);
        System.out.println("[通过验证结果,获取自定义属性]:name--" + JWTUtil.getClaimValueByJwt(jwt, "name"));
    
        System.out.println("\n====================【获取自定义属性】====================");
        System.out.println("[直接通过token,获取自定义属性]:name--" + JWTUtil.getClaimValueByToken(token, "name"));
    }
    
    3.3.2 输出结果
    ====================【生成token】====================
    [生成-token]:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlSWQiOiIxIiwibmFtZSI6IuW8oOS4iSIsImV4cCI6MTYxNTA0NjU3N30.ogV3U3dDXdo1hfZBpdr0FxvBbfjOedabNCHZZKLA2Yo
    
    ====================【验证token,并获取自定义属性】====================
    [token验证结果]:true
    [通过验证结果,获取自定义属性]:name--张三
    
    ====================【获取自定义属性】====================
    [直接通过token,获取自定义属性]:name--张三
    
    3.3.3 官网校验

    去jwt在线调试网站上校验: jwt.io Debugger

    1615036371599.png

    相关文章

      网友评论

        本文标题:JWT的基本使用

        本文链接:https://www.haomeiwen.com/subject/iabsqltx.html