初识JWT及部分源码解析

初识JWT及部分源码解析,第1张

1.JWT生成token

秘钥 这个随便整就好,主要是用于创建生成tokentoken所使用的的算法对象

private static final String JWT_SECRET = "ADSADSFGRHART1192765DMSNPOST99238S";

加密

    public static String createToken() {
        try {
            Algorithm algorithm = Algorithm.HMAC256(JWT_SECRET);
            String token = JWT.create()
                    .withClaim("userId", 10008)
                    .withClaim("userMobile", "13312312310")
                    .withClaim("userName", "li")
                    .withClaim("password", "123123")
                    .sign(algorithm);
            return token;
        } catch (Exception exception) {
            throw new BusinessException("token生成失败");
        }
    }

JWT.create()

public abstract class JWT {
    public JWT() {
    }

 ...

	// JWT.create()调用的JWTCreator.init()方法
    public static Builder create() {
        return JWTCreator.init();
    }
}

JWTCreator.init()方法

public final class JWTCreator {
	// Algorithm 算法
    private final Algorithm algorithm;
    // 头部信息 存放算法名称等
    private final String headerJson;
    // 我们传递的数据
    private final String payloadJson;
	...
	// JWTCreator.init()方法 创建了一个JWTCreator的内部类Builder 返回出去
	// 到这里JWT.create()方法就执行完了,此时的结果是JWTCreator的内部类Builder对象
    static JWTCreator.Builder init() {
        return new JWTCreator.Builder();
    }
    ...

Builder.withClaim()

  public static class Builder {
  		// 存放我们传递的数据
        private final Map payloadClaims = new HashMap();
        // 存放头部信息
        private Map headerClaims = new HashMap();

        Builder() {
        }

	// 真正的生成token的地方 对header和payload进行Base64压缩,不是加密,
	//然后对header和payload整体生成一个signature签名,这个signature才是被算法加密了的,表示数据没有被篡改过 
	private String sign() throws SignatureGenerationException {
        String header = Base64.encodeBase64URLSafeString(this.headerJson.getBytes(StandardCharsets.UTF_8));
        String payload = Base64.encodeBase64URLSafeString(this.payloadJson.getBytes(StandardCharsets.UTF_8));
        String content = String.format("%s.%s", header, payload);
        byte[] signatureBytes = this.algorithm.sign(content.getBytes(StandardCharsets.UTF_8));
        String signature = Base64.encodeBase64URLSafeString(signatureBytes);
        return String.format("%s.%s", content, signature);
    }

public JWTCreator.Builder withClaim(String name, Integer value) throws IllegalArgumentException {
			// 这里断言 存入的名称不能为空
            this.assertNonNull(name);
            this.addClaim(name, value);
            return this;
        }

	 // 这里是Builder对象的sign()方法,添加头部信息和signingKeyId 然后调用JWTCreator的sign方法生成签名并对签名进行加密
	 public String sign(Algorithm algorithm) throws IllegalArgumentException, JWTCreationException {
            if (algorithm == null) {
                throw new IllegalArgumentException("The Algorithm cannot be null.");
            } else {
                this.headerClaims.put("alg", algorithm.getName());
                this.headerClaims.put("typ", "JWT");
                String signingKeyId = algorithm.getSigningKeyId();
                if (signingKeyId != null) {
                    this.withKeyId(signingKeyId);
                }

                return (new JWTCreator(algorithm, this.headerClaims, this.payloadClaims)).sign();
            }
        }

	// 将我们的数据通过键值对的形式存放进map里 值为空时移除出去,不为空时才会存储
	 private void addClaim(String name, Object value) {
            if (value == null) {
                this.payloadClaims.remove(name);
            } else {
                this.payloadClaims.put(name, value);
            }
        }
}

生成token流程:

1.JWT.create():获取JWTCreator的内部类Builder对象
2.withClaim():校验存入数据名称不能为空,并将数据进行存储
3.Builder的sign():添加头部信息和signingKeyId
4.JWTCreator的sign():压缩头部信息和payload,对整体生成标签并加密

2.JWT解析token

 public static DecodedJWT getToken(String token) {
        if (StringUtils.isBlank(token)) {
            return null;
        }
        DecodedJWT jwt = null;
        try {
        	// jwt获取方式一
            jwt = JWT.decode(token);
            // jwt获取方式二
//          Algorithm algorithm = Algorithm.HMAC256(JWT_SECRET);
//          JWTVerifier verifier = JWT.require(algorithm).build();
//          jwt = verifier.verify(token);
		  // 获取具体的数据
//        Long userId = jwt .getClaim("userId").asLong();
//        String userMobile = jwt .getClaim("userMobile").asString();
//        String userName = jwt .getClaim("userName").asString();
//        String password = jwt .getClaim("password").asString();
            return jwt;
        } catch (Exception e) {
            return null;
        }

    }

JWT.decode()

public abstract class JWT {
    public JWT() {
    }

	// JWT.decode() 创建一个DecodedJWT对象并返回
    public static DecodedJWT decode(String token) throws JWTDecodeException {
        return new JWTDecoder(token);
    }
}
final class JWTDecoder implements DecodedJWT {
	// token信息分割后存储在parts数组中
    private final String[] parts;
    // 头部信息
    private final Header header;
    // 传递的数据
    private final Payload payload;

	// 有参构造 这里的参数jwt就是我们加生成的token
    JWTDecoder(String jwt) throws JWTDecodeException {
    	// 通过token工具类对token进行分割
        this.parts = TokenUtils.splitToken(jwt);
        // jwt转换类
        JWTParser converter = new JWTParser();

        String headerJson;
        String payloadJson;
        try {
        	// 数据转换 将压缩后的头部信息重新转换回来
        	// 分给后的第一部分就是头部信息
            headerJson = StringUtils.newStringUtf8(Base64.decodeBase64(this.parts[0]));
            // 分割后的第二部分就是我们需要的数据
            payloadJson = StringUtils.newStringUtf8(Base64.decodeBase64(this.parts[1]));
        } catch (NullPointerException var6) {
            throw new JWTDecodeException("The UTF-8 Charset isn't initialized.", var6);
        }

        this.header = converter.parseHeader(headerJson);
        this.payload = converter.parsePayload(payloadJson);
    }
}

JWT转换类:JWTPartsParser

public class JWTParser implements JWTPartsParser {
    private ObjectMapper mapper;

    public JWTParser() {
        this(getDefaultObjectMapper());
    }

    JWTParser(ObjectMapper mapper) {
        this.addDeserializers(mapper);
        this.mapper = mapper;
    }

	// 转换数据为Payload实体
    public Payload parsePayload(String json) throws JWTDecodeException {
        return (Payload)this.convertFromJSON(json, Payload.class);
    }
	// 转换头部信息为Header实体
    public Header parseHeader(String json) throws JWTDecodeException {
        return (Header)this.convertFromJSON(json, Header.class);
    }

	// 将传入的json字符串转换成传入的class类型
   T convertFromJSON(String json, Class tClazz) throws JWTDecodeException {
        JWTDecodeException exception = new JWTDecodeException(String.format("The string '%s' doesn't have a valid JSON format.", json));
        if (json == null) {
            throw exception;
        } else {
            try {
                return this.mapper.readValue(json, tClazz);
            } catch (IOException var5) {
                throw exception;
            }
        }
    }
}

getClaim():这里调用的是JWTDecoder的getClaim()方法,返回payload中存储的键的value值

public Claim getClaim(String name) {
        return this.payload.getClaim(name);
    }

两种jwt的获取方式有什么不同呢?

其实两种获取方式都差不多,第二种是包含了第一种方式,但是多了一些步骤,多了一些对jwt的验证

下面第二种获取jwt的方式,看一下源码

Algorithm algorithm = Algorithm.HMAC256(JWT_SECRET);
JWTVerifier verifier = JWT.require(algorithm).build();
jwt = verifier.verify(token);

Algorithm.HMAC256(JWT_SECRET)

这里的JWT_SECRET是我们定义的秘钥,接收方通过这个秘钥和发送方指定的加密方式来生成我们的算法实体Algorithm;HMAC256是其中的一种加密算法,有兴趣的可以去看看其他的

public static Algorithm HMAC256(String secret) throws IllegalArgumentException, UnsupportedEncodingException {
        return new HMACAlgorithm("HS256", "HmacSHA256", secret);
    }

    public static Algorithm HMAC384(String secret) throws IllegalArgumentException, UnsupportedEncodingException {
        return new HMACAlgorithm("HS384", "HmacSHA384", secret);
    }

    public static Algorithm HMAC512(String secret) throws IllegalArgumentException, UnsupportedEncodingException {
        return new HMACAlgorithm("HS512", "HmacSHA512", secret);
    }

    public static Algorithm HMAC256(byte[] secret) throws IllegalArgumentException {
        return new HMACAlgorithm("HS256", "HmacSHA256", secret);
    }

通过不同的加密方式和指定的秘钥来生成不同的Algorithm

HMACAlgorithm(String id, String algorithm, String secret) throws IllegalArgumentException, UnsupportedEncodingException {
        this(new CryptoHelper(), id, algorithm, getSecretBytes(secret));
    }

HMACAlgorithm继承于Algorithm,上面的this指向的就是下面的有参构造

class HMACAlgorithm extends Algorithm {
    private final CryptoHelper crypto;
    private final byte[] secret;

    HMACAlgorithm(CryptoHelper crypto, String id, String algorithm, byte[] secretBytes) throws IllegalArgumentException {
        super(id, algorithm);
        if (secretBytes == null) {
            throw new IllegalArgumentException("The Secret cannot be null");
        } else {
            this.secret = secretBytes;
            this.crypto = crypto;
        }
    }
 }

super(id, algorithm):调用父类的构造方法

protected Algorithm(String name, String description) {
        this.name = name;
        this.description = description;
    }

到这里,一个算法实体就创建出来了

JWT.require(algorithm):通过创建出来的算法对象,初始化一个Verification对象

public abstract class JWT {
    public JWT() {
    }
...
    public static Verification require(Algorithm algorithm) {
        return JWTVerifier.init(algorithm);
    }
 }

JWTVerifier.init(algorithm);

static Verification init(Algorithm algorithm) throws IllegalArgumentException {
        return new JWTVerifier.BaseVerification(algorithm);
    }

BaseVerification是JWTVerifier的一个内部类,继承了JWTVerifier

public static class BaseVerification implements Verification {
        private final Algorithm algorithm;
        private final Map claims;
        private long defaultLeeway;

        BaseVerification(Algorithm algorithm) throws IllegalArgumentException {
            if (algorithm == null) {
                throw new IllegalArgumentException("The Algorithm cannot be null.");
            } else {
                this.algorithm = algorithm;
                this.claims = new HashMap();
                this.defaultLeeway = 0L;
            }
        }
   }

JWTVerifier对象通过build()方法调用build(Clock clock)方法,直接通过传入的算法对象等数据new JWTVerifier(this.algorithm, this.claims, clock)对象返回

public JWTVerifier build() {
            return this.build(new ClockImpl());
        }

        public JWTVerifier build(Clock clock) {
            this.addLeewayToDateClaims();
            return new JWTVerifier(this.algorithm, this.claims, clock);
        }

前面的都是准备工作,通过秘钥和指定的算法生成一个JWTVerifier实体

verifier.verify(token);

public DecodedJWT verify(String token) throws JWTVerificationException {
		// 这里就是调用的第一种获取方式
        DecodedJWT jwt = JWT.decode(token);
        // 校验加密方式
        this.verifyAlgorithm(jwt, this.algorithm);
        // 校验Signature
        this.algorithm.verify(jwt);
        // 校验数据
        this.verifyClaims(jwt, this.claims);
        return jwt;
    }

this.verifyAlgorithm(jwt, this.algorithm):校验获取到的jwt的加密方式和发送方的加密方式是否相同

private void verifyAlgorithm(DecodedJWT jwt, Algorithm expectedAlgorithm) throws AlgorithmMismatchException {
        if (!expectedAlgorithm.getName().equals(jwt.getAlgorithm())) {
            throw new AlgorithmMismatchException("The provided Algorithm doesn't match the one defined in the JWT's Header.");
        }
    }

this.algorithm.verify(jwt):对头部信息和payload实体冲更新生成签名并加密,校验获取到的jwt的Signature和发送方的Signature是否一致,表示数据在传输过程是否被篡改

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 = this.crypto.verifySignatureFor(this.getDescription(), this.secret, contentBytes, signatureBytes);
            if (!valid) {
                throw new SignatureVerificationException(this);
            }
        } catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException var5) {
            throw new SignatureVerificationException(this, var5);
        }
    }

this.verifyClaims(jwt, this.claims):校验数据,代码有点多,这个就不贴出来了

第二种方式包含了第一种方式,但是会对获取到的jwt进行校验,校验加密算法,签名和传过来的数据

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/920014.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-16
下一篇 2022-05-16

发表评论

登录后才能评论

评论列表(0条)

保存