jjwt源码解析

jjwt源码解析

author:zxw

email:502513206@qq.com

@ Jishou University


1.前言

最近在做一个jwt授权服务,其实也就是给请求的用户生成一个token,不过关于token的验证并不在我服务负责,而是由网关去统一处理。jwt之前也用过几次,不过不是很熟悉只知道是通过base64Url算法进行加密,这次刚好用到了所以来看看jjwt的实现。

2.源码解析

在java中导入jjwt的包就可以使用包中提供的Builder生成jwt的token,以下是基础的用法。

JwtBuilder builder = Jwts.builder()
                    .setHeader(header)	  // 头部信息
                    .setId(uuid)          //id:是JWT的唯一标识
                    .setSubject(subject)  //Subject:可以存放用户信息
                    .setIssuer(appId)     //Issuer:签发者
                    .setIssuedAt(now)     //IssuedAt:jwt的签发时间
                    .signWith(signatureAlgorithm, secretKey);

通过名字可以看到,是使用的builder构造模式生成,默认给我们返回一个DefaultJwtBuilder对象

JwtBuilder

public static JwtBuilder builder() {
        return new DefaultJwtBuilder();
    }

DefaultJwtBuilder我们知道jwt是由header,payload,Signature三部分组成。

public class DefaultJwtBuilder implements JwtBuilder {

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    private Header header; // Header
    private Claims claims;
    private String payload; // 载体

    private SignatureAlgorithm algorithm; // 签名算法
    private Key                key;
    private byte[]             keyBytes;

    private CompressionCodec compressionCodec; // 压缩算法
}

Header头部由两部分信息组成,一个是类型,一个是加密的算法,具体类型如下。

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

Header在jjwt中有个Header接口表示头部,可以看到Header的固定key

public interface Header<T extends Header<T>> extends Map<String,Object> {

    /** JWT {@code Type} (typ) value: <code>"JWT"</code> */
    public static final String JWT_TYPE = "JWT";

    /** JWT {@code Type} header parameter name: <code>"typ"</code> */
    public static final String TYPE = "typ";

    /** JWT {@code Content Type} header parameter name: <code>"cty"</code> */
    public static final String CONTENT_TYPE = "cty";

    /** JWT {@code Compression Algorithm} header parameter name: <code>"zip"</code> */
    public static final String COMPRESSION_ALGORITHM = "zip";

    /** JJWT legacy/deprecated compression algorithm header parameter name: <code>"calg"</code>
     * @deprecated use {@link #COMPRESSION_ALGORITHM} instead. */
    @Deprecated
    public static final String DEPRECATED_COMPRESSION_ALGORITHM = "calg";
}

DefaultHeaderjjwt中默认会使用该类,当然我们也能自己实现Header接口或者继承该类实现自定义的Header,可以看到Header其实就是一个Map,我们也能往头部添加我们自定义的数据

public class DefaultHeader<T extends Header<T>> extends JwtMap implements Header<T> {
}

接下来就是第二部分载体了,在jjwt中我们可以传入json的payload载体也可以用claim封装我们的载体,二者只能选一个。

Claims可以看到cliams同样是个map,我们在使用setSubject等方法时,实际上就是往下面的key设置对应的Value

public interface Claims extends Map<String, Object>, ClaimsMutator<Claims> {
     /** JWT {@code Issuer} claims parameter name: <code>"iss"</code> */
    public static final String ISSUER = "iss";

    /** JWT {@code Subject} claims parameter name: <code>"sub"</code> */
    public static final String SUBJECT = "sub";

    /** JWT {@code Audience} claims parameter name: <code>"aud"</code> */
    public static final String AUDIENCE = "aud";

    /** JWT {@code Expiration} claims parameter name: <code>"exp"</code> */
    public static final String EXPIRATION = "exp";

    /** JWT {@code Not Before} claims parameter name: <code>"nbf"</code> */
    public static final String NOT_BEFORE = "nbf";

    /** JWT {@code Issued At} claims parameter name: <code>"iat"</code> */
    public static final String ISSUED_AT = "iat";

    /** JWT {@code JWT ID} claims parameter name: <code>"jti"</code> */
    public static final String ID = "jti";
}

最后一步就是Jwts.builder().signWith(signatureAlgorithm, secretKey);在该方法中指定我们的加密的算法就是头部{"alg":"HS256"}所对应的值

public JwtBuilder signWith(SignatureAlgorithm alg, Key key) {
        Assert.notNull(alg, "SignatureAlgorithm cannot be null.");
        Assert.notNull(key, "Key argument cannot be null.");
    // 加密算法,默认使用HS256
        this.algorithm = alg;
    // secret,我们与客户端规定的密钥
        this.key = key;
        return this;
    }
@Override
    public String compact() {
        // 判断载体是否为空
        if (payload == null && Collections.isEmpty(claims)) {
            throw new IllegalStateException("Either 'payload' or 'claims' must be specified.");
        }
		// 判断payload和claim是否都不为空
        if (payload != null && !Collections.isEmpty(claims)) {
            throw new IllegalStateException("Both 'payload' and 'claims' cannot both be specified. Choose either one.");
        }
		// key和keybytes是否都不为空
        if (key != null && keyBytes != null) {
            throw new IllegalStateException("A key object and key bytes cannot both be specified. Choose either one.");
        }
		// 如果为空,生成默认的Header
        Header header = ensureHeader();

        Key key = this.key;
        // 如果未指定密钥算法,则生成默认的算法
        if (key == null && !Objects.isEmpty(keyBytes)) {
            key = new SecretKeySpec(keyBytes, algorithm.getJcaName());
        }

        JwsHeader jwsHeader;

        if (header instanceof JwsHeader) {
            jwsHeader = (JwsHeader)header;
        } else {
            jwsHeader = new DefaultJwsHeader(header);
        }
		// 设置头部的alg对应的算法类型
        if (key != null) {
            jwsHeader.setAlgorithm(algorithm.getValue());
        } else {
            //no signature - plaintext JWT:
            jwsHeader.setAlgorithm(SignatureAlgorithm.NONE.getValue());
        }
		// 设置压缩算法
        if (compressionCodec != null) {
            jwsHeader.setCompressionAlgorithm(compressionCodec.getAlgorithmName());
        }
		// 头部进行base64Url加密
        String base64UrlEncodedHeader = base64UrlEncode(jwsHeader, "Unable to serialize header to json.");

        String base64UrlEncodedBody;
		// 对载体进行base64Url加密
        if (compressionCodec != null) {

            byte[] bytes;
            try {
                bytes = this.payload != null ? payload.getBytes(Strings.UTF_8) : toJson(claims);
            } catch (JsonProcessingException e) {
                throw new IllegalArgumentException("Unable to serialize claims object to json.");
            }

            base64UrlEncodedBody = TextCodec.BASE64URL.encode(compressionCodec.compress(bytes));

        } else {
            // 加密
            base64UrlEncodedBody = this.payload != null ?
                    TextCodec.BASE64URL.encode(this.payload) :
                    base64UrlEncode(claims, "Unable to serialize claims object to json.");
        }

        String jwt = base64UrlEncodedHeader + JwtParser.SEPARATOR_CHAR + base64UrlEncodedBody;
		// 对签名进行base64Url加密
        if (key != null) { //jwt must be signed:
			// 生成Signer,下发给客户端的密钥
            JwtSigner signer = createSigner(algorithm, key);
			// 对前两部分进行base64Url加密
            String base64UrlSignature = signer.sign(jwt);
			// 组合
            jwt += JwtParser.SEPARATOR_CHAR + base64UrlSignature;
        } else {
            // no signature (plaintext), but must terminate w/ a period, see
            // https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25#section-6.1
            jwt += JwtParser.SEPARATOR_CHAR;
        }
		// 返回token
        return jwt;
    }

可以看到jwt的组成,对header进行base64Url加密 + payload的base64Url加密 + (header + payload + secret)三部分加密组成。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值