Java编程系列JWT秘钥生成
Posted 善良勤劳勇敢而又聪明的老杨
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java编程系列JWT秘钥生成相关的知识,希望对你有一定的参考价值。
热门系列:
【算法系列】实战篇:Diffie-Hellman算法实现通信秘钥流程
目录
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秘钥生成的主要内容,如果未能解决你的问题,请参考以下文章
算法系列实战篇:Diffie-Hellman算法实现通信秘钥流程