您好,欢迎访问代理记账网站
  • 价格透明
  • 信息保密
  • 进度掌控
  • 售后无忧

Java手动生成JWT

头部

首先我们需要一个Json

{
	"alg":"HS256"
}

接着我们需要将其在Java中展示出来

        String header = "{\n" +
                "\t\"alg\":\"HS256\"\n" +
                "}";

之后要去除所有空格

        String header = "{"alg":"HS256"}";

报错了,很明显是有问题的,接着用转义字符将“正常展示出来

        String header = "{\"alg\":\"HS256\"}";

此时我们将这个字符串用Base64URL加密,这里使用基于Commons Codec的URLBase64加密

        <!--Commons Codec依赖-->
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.15</version>
        </dependency>
        byte[] encodeHeaderBytes = Base64.encodeBase64URLSafe(header.getBytes("UTF-8"));

然后把byte数组转为字符串

		String encodeHeader = new String(encodeHeaderBytes);

此时可以尝试输出此字符串,理论上应当如下,这也是此JWT的头部

		eyJhbGciOiJIUzI1NiJ9

JWT官网调试器调试无误,可以正常解析JWT标头
在这里插入图片描述

载荷

首先随便来个有内容的JSON,如下

{
	"sub":"可达鸭"
}

之后和头部做同样的处理,最终输出的内容应当如下

eyJzdWIiOiLlj6_ovr7puK0ifQ

然后将头部和载荷用.拼接,如下

        String s = encodeHeader+"."+encodeClaims;
        //输出结果eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiLlj6_ovr7puK0ifQ

JWT官网调试器调试无误,可以正常解析JWT标头及载荷
在这里插入图片描述

签名

首先我们需要一个自己生成的密钥,或者说加密盐,他可以只是一个简单的123456,也可以是经过复杂处理的字符串

        String secret = "123456";//普通密钥

之后用HmacSHA256对之前拼接好的字符串加密,并用Base64URL进行编码

        String message = encodeHeader + "." + encodeClaims;
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        String verifySignature = new String(Base64.encodeBase64URLSafe(sha256_HMAC.doFinal(message.getBytes())));

最终将标头和载荷、签名通过.进行拼接,就得到了以下字符串

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiLlj6_ovr7puK0ifQ.2AHwp9OhaBwj2uNlXTGSzyaIfym7hrA2_ubRx4E9xd4

将其放入JWT官网,验证通过并且解析无误
在这里插入图片描述

答疑

这里是本人在进行手动生成JWT时遇到的问题
1.Base64加密头部和官方加密有较小差距
解决:
官方使用的是Base64URL加密,并不是Base64加密
Base64URL加密对比Base64加密增强了对浏览器的适应性,Base64加密时加密为+的转为-(减法符号),加密为/的转为_(下划线)并且去掉末尾的=(等号)
2.使用Base64URL加密后数据依然有出入
解决:
模拟的JSON数据双引号使用’进行替代了,替换为"转义就可以了
3.签名字符完全错误
解决:
原本的做法是对HmacSHA256加密后的签名直接进行toHexString的方式将其显示出来(HmacSHA256加密的byte数组正常无法显示),后来发现官方的做法是对此byte数组进行Base64URL加密,修改后无错
4.官方调试器下的secret base64 encode(此问题浪费了我2小时的生命)
解决:
从始至终我都认为这个选项勾选上的意思是,这个选项的意思是
原本我的secret(盐)值为123456,勾选上后会将123456进行base64加密再作为secret进行Base64URL加密,相信大部分人都是这么认为的。
但是我在代码上重现了这一操作后发现和官方得到的JWT签名完全是不同的。
于是我有了另一个猜想,勾选此选项后是否不再对最后得到的byte数组进行base64URL加密,后经过验证,猜想错误。
之后又尝试了将很多不正常的想法,花费了大量的时间,在我快准备放弃的时候,我想起了百度,于是我百度了一下,JWT官网的secret base64 encode是什么意思,以下是得到的答案:
JWT官网的secret base64 encode意思是将secret视为经过base64加密产生的,先对其进行base64decode(解密)操作,再将其作为secret进行HmacSHA256加密,测试无误。

        Base64 base64 = new Base64();
        byte[] secret = new Base64().encode("123456".getBytes("UTF-8"));//复杂密钥
        System.out.println("Base64加密盐:" + new String(secret));
        //Base64加密盐:MTIzNDU2

        String message = encodeHeader + "." + encodeClaims;
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(secret, "HmacSHA256");
        sha256_HMAC.init(secret_key);
        String verifySignature = new String(Base64.encodeBase64URLSafe(sha256_HMAC.doFinal(message.getBytes())));
        System.out.println("Base64加密盐最终签名:" + verifySignature);
        //Base64加密盐最终签名:V7TXJxLIij3gduj6b_8oGQ6K_RmA3kd43S8RrOB554s
        
        secret_key = new SecretKeySpec("123456".getBytes("UTF-8"),"HmacSHA256");
        sha256_HMAC.init(secret_key);
        verifySignature = new String(Base64.encodeBase64URLSafe(sha256_HMAC.doFinal(message.getBytes())));
        System.out.println("123456加密盐最终签名:" + verifySignature);
        //123456加密盐最终签名:2AHwp9OhaBwj2uNlXTGSzyaIfym7hrA2_ubRx4E9xd4

在这里插入图片描述
在这里插入图片描述
最后更新于2021年4月17日
原创不易,如果该文章对你有所帮助,望左上角点击关注~如有任何技术相关问题,可通过评论联系我讨论,我会在力所能及之内进行相应回复以及开单章解决该问题.

该文章如有任何错误请在评论中指出,感激不尽,转载请附出处!
*个人博客首页:https://blog.csdn.net/yjrguxing ——您的每个关注和评论都对我意义重大


分享:

低价透明

统一报价,无隐形消费

金牌服务

一对一专属顾问7*24小时金牌服务

信息保密

个人信息安全有保障

售后无忧

服务出问题客服经理全程跟进