Java编程系列JWT秘钥生成

Posted 善良勤劳勇敢而又聪明的老杨

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java编程系列JWT秘钥生成相关的知识,希望对你有一定的参考价值。

 热门系列:

【算法系列】实战篇:Diffie-Hellman算法实现通信秘钥流程


目录

1、JWT简介

2、JWT的优缺点

3、JWT组成部分

4、JWT的使用

        4.1 生成公钥私钥命令

        4.2 JWT生成


1、JWT简介

        JWT其全称是JSON Web Token,也是经常作为一种安全的token使用。通俗地说,JWT的本质就是一个字符串,它是将用户信息保存到一个Json字符串中,然后进行编码后得到一个JWT token,并且这个JWT token带有签名信息,接收后可以校验是否被篡改,所以可以用于在各方之间安全地将信息作为Json对象传输。

2、JWT的优缺点

优点:
1:无状态 - token 自身包含了身份验证所需要的所有信息,使得我们的服务器不需要存储Session信息,这显然增加了系统的可用性和伸缩性,大大减轻了服务端的压力
2:不受语种/平台限制 - 一个Token可以同时被多个不同语言/平台的后端接收认证
3:不依赖于Cookie(虽然JWT的存储方式还是建议使用HttpOnly+Secure方式存储,但毕竟有些人就是不喜欢Cookie)
4:性能问题(完善的客户端使用JWT可以减少与服务器间的通讯次数,像性别爱好这种非敏感信息,认证一次JWT完全可以LocalStorage存储)
5:Decoupled/Decentralized - JWT可以在任何地方生成,认证也能在任何地方

缺点:
1.没法让单独的一个 JWT token 失效(通常的解决方案是在服务端存储jwt token,但如此一来 就把无状态变成了有状态)
2.当一个用户更新了自己的个人信息之后,之前签发的 JWT token 中的信息将会变得过时

3、JWT组成部分

jwt主要由3部分组成

标头(Header):是一个描述JWT元数据的JSON对象,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT
有效载荷(Payload):是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据
签名(Signature):是对上面两部分数据签名,需要使用base64编码后的header和payload数据,通过指定的算法生成哈希,以确保数据不会被篡改

4、JWT的使用

4.1 生成公钥私钥命令

通过使用如下命令,生成公私钥

keytool -genkeypair -alias testKey -keyalg RSA -keypass testPwd -keystore testkey.jks -storepass testStoreKey

  • -alias:密钥的别名
  • -keyalg:使用的hash算法
  • -keypass:密钥的访问密码
  • -keystore:密钥库文件名 
  • -storepass:密钥库的访问密码

在testkey.jks文件中获取公钥public.jks文件

keytool -export -alias testKey -keystore testkey.jks -file public.jks

4.2 JWT生成

先引入java依赖:

<dependency>
      <groupId>io.jsonwebtoken</groupId>
      <artifactId>jjwt</artifactId>
      <version>0.9.1</version>
</dependency>

实践代码:

package xxx.xxx.xxx.xxx.util;
 
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;
 
import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
 

public class JwtUtils 
 
    /**
     * 获取生成的私钥
     *
     * @return
     */
    public RSAPrivateKey generalPrivateKey() 
        KeyPair keyPair = this.generalKeyPair();
        return (RSAPrivateKey) keyPair.getPrivate();
    
 
    public KeyPair generalKeyPair() 
        // 本地的密码解码
        // 根据给定的字节数组使用RS256加密算法构造一个密钥
        //创建一个密钥工厂
        //私钥的位置  本模块中resources中的testkey.jks就是私钥,public.jks就是公钥
        ClassPathResource classPathResource = new ClassPathResource("testkey.jks");
        //密钥库的密码
        String keyPass = "testStoreKey";
        //参数1 私钥的位置 参数2 密钥库的密码
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(classPathResource, keyPass.toCharArray());
        //基于工厂获取私钥
        //密钥的别名
        String alias = "testKey";
        //密钥的密码
        String password = "testPwd";
        //参数1 密钥的别名  参数2 密钥的密码
        KeyPair keyPair = keyStoreKeyFactory.getKeyPair(alias, password.toCharArray());
        return keyPair;
    
 
    /**
     * 获取生成的公钥
     *
     * @return
     */
    public RSAPublicKey generalPublicKey() 
        KeyPair keyPair = this.generalKeyPair();
        return (RSAPublicKey) keyPair.getPublic();
    
 
    /**
     * 通过私钥生成一个JWT的token 用于以后解析验证
     * @param id
     * @param issuer
     * @param aud
     * @param subject
     * @return
     */
    public String createJWT(String id, String issuer, String aud, String subject) 
 
        // 指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.RS256;
 
        // 生成JWT的时间
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
 
        // 创建payload的私有声明(根据特定的业务需要添加,如果要拿这个做验证,一般是需要和jwt的接收方提前沟通好验证方式的)
        Map<String, Object> claims = new HashMap<>();
        claims.put("alg", "RS256");
        claims.put("typ", "JWT");
        long expMillis = nowMillis + 3600000;
        Date expDate = new Date(expMillis);
        // 生成签名的时候使用的秘钥secret,切记这个秘钥不能外露哦。它就是你服务端的私钥,在任何场景都不应该流露出去。
        // 一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
        // 下面就是在为payload添加各种标准声明和私有声明了
        JwtBuilder builder = Jwts.builder() // 这里其实就是new一个JwtBuilder,设置jwt的body
                .setHeader(claims)          // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
                .setId(id)                  // 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
                .setIssuedAt(now)           // iat: jwt的签发时间
                .setIssuer(issuer)          // issuer:jwt签发人
                .setAudience(aud)           // 接收jwt的一方
                .setExpiration(expDate)     // 到期时间
                .setSubject(subject)        // sub(Subject):代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可以存放什么userid,roldid之类的,作为什么用户的唯一标志。
                .signWith(signatureAlgorithm, generalPrivateKey()); // 设置签名使用的签名算法和签名使用的秘钥
        return builder.compact();
    

生成的字符串内容类似:

eyJ0eXAiOiJKV1QiLCJhbGciOiJsdJ9.eyJqdGkiOiJjYjc4M2I5Yi1kMGYxLTRjMWEtYmQyMC0zOTMwYmE2YmM0YmYiLCJpYXQiOjE2NjIzNTE1ODIsImlzcyI6Inlhbmd5IiwiYXVkIjoieWFuZyIsImV4cCI6MTY2MjM1NTE4Miwic3ViIjoiMTIzIn0.GhCsMYMzxIDtCK878zIhRQAO-MougWlkW3zUCuARZ8qbcoRX1DmierttyyBahTHi7aOlMFocd40x9p7VSBD1PNomwcbnKELQ6D0D1UVE-7b-2EjALj1jwiwkEZyYFTZBNGlrYkyEV2KFsoYrggCFyZb8BBejt_hOaIc2QzcaBXlP1Mcb-mdeTBDsDJnwGVTP0HY6d0GAJTPc8WF-WSwgewp_FCfZfvdgwCtc9DfLMITqu3Zwk3P4A6ObCxMxchdfgghhu37gbp_-AAY_Tr_3yTKQMt0V-0V35hFKyXVhkKSvoa6zjxUx1tAc_bXeDG-WQRUXgS_f_YH8w

        jwt的记录分享就到这里啦,欢迎下方留言讨论,有问必答~~~

以上是关于Java编程系列JWT秘钥生成的主要内容,如果未能解决你的问题,请参考以下文章

Java编程系列JWT秘钥生成

java并发编程性能与可伸缩性

JWT

算法系列实战篇:Diffie-Hellman算法实现通信秘钥流程

算法系列实战篇:Diffie-Hellman算法实现通信秘钥流程

jwt 工具类