美文网首页
JWT java 源码浅析

JWT java 源码浅析

作者: dwwl | 来源:发表于2019-09-29 17:04 被阅读0次

    JWT场景优劣:
    http://blog.didispace.com/learn-how-to-use-jwt-xjf/

    JWT入门教程:
    https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html

    JWTUtil.java

    package org.inlighting.util;
    
    import com.auth0.jwt.JWT;
    import com.auth0.jwt.JWTVerifier;
    import com.auth0.jwt.algorithms.Algorithm;
    import com.auth0.jwt.exceptions.JWTDecodeException;
    import com.auth0.jwt.interfaces.DecodedJWT;
    
    import java.io.UnsupportedEncodingException;
    import java.util.Date;
    
    public class JWTUtil {
    
        // 过期时间5分钟
        private static final long EXPIRE_TIME = 5*60*1000;
    
        /**
         * 校验token是否正确
         * @param token 密钥
         * @param secret 用户的密码
         * @return 是否正确
         */
        public static boolean verify(String token, String username, String secret) {
            try {
                Algorithm algorithm = Algorithm.HMAC256(secret);
                JWTVerifier verifier = JWT.require(algorithm)
                        .withClaim("username", username)
                        .build();
                DecodedJWT jwt = verifier.verify(token);
                return true;
            } catch (Exception exception) {
                return false;
            }
        }
    
        /**
         * 获得token中的信息无需secret解密也能获得
         * @return token中包含的用户名
         */
        public static String getUsername(String token) {
            try {
                DecodedJWT jwt = JWT.decode(token);
                return jwt.getClaim("username").asString();
            } catch (JWTDecodeException e) {
                return null;
            }
        }
    
        /**
         * 生成签名,5min后过期
         * @param username 用户名
         * @param secret 用户的密码
         * @return 加密的token
         */
        public static String sign(String username, String secret) {
            try {
                Date date = new Date(System.currentTimeMillis()+EXPIRE_TIME);
                Algorithm algorithm = Algorithm.HMAC256(secret);
                // 附带username信息
                return JWT.create()
                        .withClaim("username", username)
                        .withExpiresAt(date)
                        .sign(algorithm);
            } catch (UnsupportedEncodingException e) {
                return null;
            }
        }
    }
    
    

    加密过程:
    JWT.create().withClaim("username", username).withExpiresAt(date).sign(algorithm)

    create()

    com.auth0.jwt.JWT#create 
    public static JWTCreator.Builder create() {
            return JWTCreator.init();
        }
    
    com.auth0.jwt.JWTCreator#init
    static JWTCreator.Builder init() {
        return new Builder();
    }
    
     public static class Builder {
            private final Map<String, Object> payloadClaims;
            private Map<String, Object> headerClaims;
    
            Builder() {
                this.payloadClaims = new HashMap<>();
                this.headerClaims = new HashMap<>();
            }
            ...
        }
    

    withClaim(key,value)

    com.auth0.jwt.JWTCreator.Builder#addClaim 该方法有多个重载形式,即value可为
    Boolean Integer Long Double String Date
    //其中addClaim方法 将键值对加入到payload里面
    public Builder withClaim(String name, String value) throws IllegalArgumentException {
        assertNonNull(name);
        addClaim(name, value);
        return this;
    }
    
    //在payload中添加一个键为exp 值为过期时间的键值对
    public Builder withExpiresAt(Date expiresAt) {
        addClaim(PublicClaims.EXPIRES_AT, expiresAt);
        return this;
    }
    

    sign(algorithm)

    com.auth0.jwt.JWTCreator.Builder#sign
    //该方法时进行JWTCreator对象的建造,并调用JWTCreator中的方法生成最终的字符串,在这个方法里对向jwt的头添加
    //键为alg 值为加密名称和键为typ 值为"JWT",将JWTCreator建造者中的算法对象,表示jwt header的map对象,表示jwt payload的map对象传入JWTCreator 
    public String sign(Algorithm algorithm) throws IllegalArgumentException, JWTCreationException {
         if (algorithm == null) {
             throw new IllegalArgumentException("The Algorithm cannot be null.");
         }
         headerClaims.put(PublicClaims.ALGORITHM, algorithm.getName());
         headerClaims.put(PublicClaims.TYPE, "JWT");
         String signingKeyId = algorithm.getSigningKeyId();
         if (signingKeyId != null) {
             withKeyId(signingKeyId);
         }
         return new JWTCreator(algorithm, headerClaims, payloadClaims).sign();
     }
    
    com.auth0.jwt.JWTCreator#sign
    //将传入的JWT 的header payload信息进行Base64编码,JWT的signature用算法对header payload组成的字符串进行加密生成签名,拼接其后,这就是最后的结果
    private String sign() throws SignatureGenerationException {
        String header = Base64.encodeBase64URLSafeString(headerJson.getBytes(StandardCharsets.UTF_8));
        String payload = Base64.encodeBase64URLSafeString(payloadJson.getBytes(StandardCharsets.UTF_8));
        String content = String.format("%s.%s", header, payload);
        byte[] signatureBytes = algorithm.sign(content.getBytes(StandardCharsets.UTF_8));
        String signature = Base64.encodeBase64URLSafeString((signatureBytes));
        return String.format("%s.%s", content, signature);
    }
    

    解密:
    JWT.require(algorithm).withClaim("username", username).build()

    verifier.verify(json);

     com.auth0.jwt.JWTVerifier#verify
    
    
     public DecodedJWT verify(String token) throws JWTVerificationException {
     //JWTParser对jwt解析,由base64解析为原字符串,即jwt对象包含jwt的header和payload信息
            DecodedJWT jwt = JWT.decode(token);
     //判断加密方式是否正确
            verifyAlgorithm(jwt, algorithm);
     //利用构建JWTVerifier对象传入的Algorithm对象,即Algorithm.HMAC256("test123"),对jwt中的header和payload进行加密,判断加密后的得到的签名和当前的是否相等
            algorithm.verify(jwt);
     //进行过期时间等判断
            verifyClaims(jwt, claims);
            return jwt;
        }
    
    
        public void verify(DecodedJWT jwt) throws SignatureVerificationException {
            byte[] contentBytes = String.format("%s.%s", jwt.getHeader(), jwt.getPayload()).getBytes(StandardCharsets.UTF_8);
            byte[] signatureBytes = Base64.decodeBase64(jwt.getSignature());
    
            try {
                boolean valid = crypto.verifySignatureFor(getDescription(), secret, contentBytes, signatureBytes);
                if (!valid) {
                    throw new SignatureVerificationException(this);
                }
            } catch (IllegalStateException | InvalidKeyException | NoSuchAlgorithmException e) {
                throw new SignatureVerificationException(this, e);
            }
        }
    
    private void verifyClaims(DecodedJWT jwt, Map<String, Object> claims) throws TokenExpiredException, InvalidClaimException {
        for (Map.Entry<String, Object> entry : claims.entrySet()) {
            switch (entry.getKey()) {
                case PublicClaims.AUDIENCE:
                    //noinspection unchecked
                    assertValidAudienceClaim(jwt.getAudience(), (List<String>) entry.getValue());
                    break;
                case PublicClaims.EXPIRES_AT:
                    assertValidDateClaim(jwt.getExpiresAt(), (Long) entry.getValue(), true);
                    break;
                case PublicClaims.ISSUED_AT:
                    assertValidDateClaim(jwt.getIssuedAt(), (Long) entry.getValue(), false);
                    break;
                case PublicClaims.NOT_BEFORE:
                    assertValidDateClaim(jwt.getNotBefore(), (Long) entry.getValue(), false);
                    break;
                case PublicClaims.ISSUER:
                    assertValidStringClaim(entry.getKey(), jwt.getIssuer(), (String) entry.getValue());
                    break;
                case PublicClaims.JWT_ID:
                    assertValidStringClaim(entry.getKey(), jwt.getId(), (String) entry.getValue());
                    break;
                case PublicClaims.SUBJECT:
                    assertValidStringClaim(entry.getKey(), jwt.getSubject(), (String) entry.getValue());
                    break;
                default:
                    assertValidClaim(jwt.getClaim(entry.getKey()), entry.getKey(), entry.getValue());
                    break;
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:JWT java 源码浅析

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