jwt《token》
阅读原文时间:2023年07月08日阅读:4

payload与claims只能存在一个
这部分是jwt源码:
依赖如下:官方文档的依赖

io.jsonwebtoken jjwt-api 0.11.2

io.jsonwebtoken jjwt-impl 0.11.2

io.jsonwebtoken jjwt-jackson 0.11.2

2020-11-1217:17:43
@Override
public String compact() {

    if (this.serializer == null) {  
        // try to find one based on the services available  
        // TODO: This util class will throw a UnavailableImplementationException here to retain behavior of previous version, remove in v1.0  
        // use the previous commented out line instead  
        this.serializer = LegacyServices.loadFirst(Serializer.class);  
    }

    if (payload == null && Collections.isEmpty(claims)) {//判断空  
        payload = "";  
    }

    if (payload != null && !Collections.isEmpty(claims)) {//判断空  
        throw new IllegalStateException("Both 'payload' and 'claims' cannot both be specified. Choose either one.");  
    }

    Header header = ensureHeader();

    JwsHeader jwsHeader;  
    if (header instanceof JwsHeader) {//判断header类型  
        jwsHeader = (JwsHeader) header;  
    } else {  
        //noinspection unchecked  
        jwsHeader = new DefaultJwsHeader(header);  
    }

    if (key != null) {     //判断加密方式  
        jwsHeader.setAlgorithm(algorithm.getValue());  
    } else {  
        //no signature - plaintext JWT:  
        jwsHeader.setAlgorithm(SignatureAlgorithm.NONE.getValue());  
    }

    if (compressionCodec != null) {  
        jwsHeader.setCompressionAlgorithm(compressionCodec.getAlgorithmName());  
    }  
       //header进行base64加密  
    String base64UrlEncodedHeader = base64UrlEncode(jwsHeader, "Unable to serialize header to json.");  
     //绿色部分表示payload转化base64加密成字节数组进行一次压缩  
    byte\[\] bytes;  
    try {  
        bytes = this.payload != null ? payload.getBytes(Strings.UTF\_8) : toJson(claims);  
    } catch (SerializationException e) {  
        throw new IllegalArgumentException("Unable to serialize claims object to json: " + e.getMessage(), e);  
    }

    if (compressionCodec != null) {  
        bytes = compressionCodec.compress(bytes);  
    }  
      //对payload压缩后的租界数组再来一次base64加密  
    String base64UrlEncodedBody = base64UrlEncoder.encode(bytes);  
 //签名拼接  
    String jwt = base64UrlEncodedHeader + JwtParser.SEPARATOR\_CHAR + base64UrlEncodedBody;  


    // 加密的key再次判断为不为空  
    if (key != null) { //jwt must be signed:  
      //签名与拼接  
        JwtSigner signer = createSigner(algorithm, key);

        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;  
    }

    return jwt;  
}

个人模拟加密流程:

    //0  原始签名  
    String header0 = "{\\"alg\\":\\"HS256\\"}";  
    String claims0 = "{\\"sub\\":\\"wangbiao\\",\\"aud\\":\\"wangbiao\\"}";

    Encoder<byte\[\], String> base64UrlEncoder = Encoders.BASE64URL;  
    String hed =base64UrlEncoder.encode(header0.getBytes("UTF-8"));

    Map map= (Map) JSON.parse(claims0);  
    Serializer<Map<String,?>> serializer= LegacyServices.loadFirst(Serializer.class);  
    byte\[\] clambyte0= serializer.serialize(map);

    /\*\*  
     \* payload为空String方法转化字节数组,不然转化JSON序列化,默认payload为null 即上面的序列化  
     \* bytes = this.payload != null ? payload.getBytes(Strings.UTF\_8) : toJson(claims);  
     \* byte \[\] claimbyte=claims0.getBytes("UTF-8");  
     \*/

    /\*\*  
     \*  默认jwt不需要压缩  
     \*        DeflateCompressionCodeWb deflateCompressionCodecss=new DeflateCompressionCodeWb();  
     \*        claims压缩字节  
     \*        byte\[\]  clambyte1 =deflateCompressionCodecss.doCompress(clambyte0);  
     \*/

// KeyPair keyPair = Keys.keyPairFor(SignatureAlgorithm.RS256);
//签名key
SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256);

    //字节在编码  
    String cla= base64UrlEncoder.encode(clambyte0);  
    String concatenated= hed+'.'+cla;

    //签名方式  
    SignatureAlgorithm algorithm=SignatureAlgorithm.HS256;

    JwtSigner defaultJwtBuilder=new DefaultJwtSigner(algorithm,key,base64UrlEncoder);

    String base64UrlSignature = defaultJwtBuilder.sign(concatenated);  
    String sss= concatenated+'.'+base64UrlSignature;  

// System.out.println(sss);
//验签
System.out.println(Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(sss));

利用jwt封装的方法:

    //2设置token方式二 设置头  
    Header header = new DefaultHeader();  
    header.put("alg", "HS256");  

// //header设置方式1 设置map
Map claims = new HashMap<>();
claims.put("aud", "wangbiao");
claims.put("sub", "wangbiao");
String jwss = Jwts.builder()
.setHeader((Map) header)
.setClaims(claims)
.signWith(key)
.compact();
// System.out.println(jwss);
//验签
System.out.println(Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(jwss));

个人jwt生成结果:

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ3YW5nYmlhbyIsImF1ZCI6IndhbmdiaWFvIn0.Lxso52KaEnj_a7mYAoH0eoPisIpCo_2sBihmf7sHxGw
jwt封装生成结果:

eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJ3YW5nYmlhbyIsInN1YiI6IndhbmdiaWFvIn0.weI8QvS16782iZR6Hb3k3kYExmpSyax0a-KRzfG-vFc
可以看出最后签名不一致

个人jwt验签解析:

header={alg=HS256},body={sub=wangbiao, aud=wangbiao},signature=Lxso52KaEnj_a7mYAoH0eoPisIpCo_2sBihmf7sHxGw

jwt封装眼前解析:

header={alg=HS256},body={aud=wangbiao, sub=wangbiao},signature=weI8QvS16782iZR6Hb3k3kYExmpSyax0a-KRzfG-vFc

思考:通过追踪源码:发现默认情况下payload没有进行压缩

即没有进行如下操作:

if (compressionCodec != null) {
bytes = compressionCodec.compress(bytes);
}

           payload为bull进行字符String转化为字节数组,不然转化为转化为tojSON(claims)序列化  

bytes = this.payload != null ? payload.getBytes(Strings.UTF_8) : toJson(claims);

         header正常进行base64加密  

猜想:我此处使用main方法测试,同一个线程下jwt签名具有不可重复,或者jwt默认就是签名不可重复

header={alg=HS256},body={aud=wangbiao, sub=wangbiao},signature=weI8QvS16782iZR6Hb3k3kYExmpSyax0a-KRzfG-vFc

官方文档有如下说明:意思就是如果在一个系统中使用不同的key需要自己重写,密匙解析器,好处就是方便不同的业务使用不同的加密方法,利用适配器的思想解决不同类型的key从而进行验签

:看源码太累,有哪位大神按照如下思路出个方案,共同进步

签名密钥分解器
如果您的应用程序期望使用不同的键签名的JWSs,您就不会调用setSigningKey方法。相反,您需要实现SigningKeyResolver接口,并通过setSigningKeyResolver方法在JwtParserBuilder上指定一个实例。
例如:
SigningKeyResolver signingKeyResolver = getMySigningKeyResolver();

Jwts.parserBuilder ()

.setSigningKeyResolver (signingKeyResolver) / / <—

.build ()
.parseClaimsJws (jwsString);
通过扩展SigningKeyResolverAdapter并实现resolveSigningKey(JwsHeader, Claims)方法,可以简化一些事情。例如:
public class MySigningKeyResolver extends SigningKeyResolverAdapter {

@Override  
public Key resolveSigningKey(JwsHeader jwsHeader, Claims claims) {  
    // implement me  
}  

}
JwtParser将在解析JWS JSON之后,但在验证JWS签名之前调用resolveSigningKey方法。这允许您检查JwsHeader并声明任何可以帮助您查找用于验证特定jws的键的信息。这对于具有更复杂安全模型的应用程序非常强大,这些应用程序可能在不同的时间使用不同的密钥,或者针对不同的用户或客户。
您可能会检查哪些数据?
JWT规范支持的方法是在创建JWS时在JWS头部设置一个kid (Key ID)字段,例如:

getSigningKey();

getKeyId(signingKey);//任何将密钥与ID关联的机制都可以

jws .builder()

.setHeaderParam (JwsHeader。KEY_ID, keyId) // 1

.signWith (signingKey) / / 2

.compact ();
然后在解析期间,SigningKeyResolver可以检查JwsHeader以获取kid,然后使用该值从某个地方(比如数据库)查找键。例如:
public class MySigningKeyResolver extends SigningKeyResolverAdapter {

@Override  
public Key resolveSigningKey(JwsHeader jwsHeader, Claims claims) {

    //inspect the header or claims, lookup and return the signing key

    String keyId = jwsHeader.getKeyId(); //或任何其他字段,你需要检查  
    Key key = lookupVerificationKey(keyId); //实现我

    return key;  
}  

}
注意,检查jwsHeader.getKeyId()只是查找键的最常见方法—您可以检查任意数量的头字段或声明,以确定如何查找验证键。这完全取决于JWS是如何创建的。
最后请记住,对于HMAC算法,返回的验证密钥应该是一个秘钥,而对于非对称算法,返回的密钥应该是一个公钥(而不是一个私钥)。

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器