- 引入 nimbus-jose-jwt
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>9.23</version>
</dependency>
该包可以使用rsa算法进行jwt加密
2. 使用openssl生成密钥
生成RSA加密私钥
代码语言:javascript复制openssl genrsa -aes256 -passout pass:123456 -out rsa_aes_private.key 1024
使用RSA私钥生成公钥
代码语言:javascript复制openssl rsa -in rsa_aes_private.key -pubout -out rsa_public.key
因为使用 openssl 生成的密钥是 pkcs1格式的密钥,java默认只能使用 pkcs8 格式的密钥,所以需要进行pkcs1到pkcs8转换的转换
代码语言:javascript复制openssl pkcs8 -topk8 -in rsa_aes_private.key -inform pem -out pkcs8_rsa_private.key -outform pem -nocrypt
3. 在application.yml增加配置
代码语言:javascript复制certificate:
useKid: k1
certificates:
- kid: k1
privateKey: |
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIC3TBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQImUIM57O4TH4CAggA
MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBDJkNNh8w3fTcQjKP3A6oVHBIIC
gC7Nuk2xzW2 CHycQ5InCB76u/C1L6jTKC8M7XgAhacM7WfQHHfJFjMsN9J94vwd
8rDlTPE nNHmLw386fBFtwDTLC8cuALmcvzH qxYVXD5ygYGRrclUulOiRwiZ5f4
TjdmHApP15SbglG/B4tV5ERa2nudccXDdg7fAJsqlaZsqLGnPxYBhbUwE428DFjn
MkyA2N06AQzyU7aFYeuKGSS5D04HRAyZ/SBVUg4lBXI34TAZGG447LhHxXuorBgH
N/JJpHGgQyURmH43HI4bpiPnXHbHTRNYUehQGUI/oNWAZugFLFrXnYl120 wkca
U8zQu/23uhy 4iCuy5SnNxdOKvSNpBTIh2BEbEm8nmHvbcfg5pcgExb/g7rnFWPP
ryNdR42Vm5Wp4xrzFT71WwWSUVkC1N037QH0K09BTcJi/XV6qxxOtLSfq2uzTJQ2
vIs9VGgKy9IAlIa6aur1Th/cpbQ dz9ld1ZYWHgBxw8hFxZkbu qZUeAo6c1pHZI
rwPvYj06BK5R2xkrMYcJaEasJz0PrvxMzk9 0qSJNdT y9nzaxLN /ypldm3DarH
ZiyG5QC/TJTWkckM0AIdZujLIs8j3IQc4Sp21zrjFeMBzVd3CJBGgaFAV3o6CaAp
9OJYytj/cNAy1jEfTl7AbaRAbteBbSFQdAsSGqgC0u JpyncH1r3YoM720HIB7Xa
pLyOCA3zWcbKPwHTBlH1x7 ppXy/zvdAwmUlTydD1aaa2i4bv2 ctdjWhMW77Nxp
TE3y5Kim8CSW885PgIRxKocU0DgeOEtPmuOxxMjbouSF18mSmZP8NmoiVMpf/cS9
9c4FlRjxWiPoRY EMWk81cU=
-----END ENCRYPTED PRIVATE KEY-----
publicKey: |
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1iRRYvvbs7cPGfpJuND0ArkzE
MTzeDG0OuTxcdl A6yOB6jRRe/9fqRRZJSZOcrvr8I2YKs9n DGo2nGUrmcRlav5
s77g7jGJmOW6ZvNBBU4g w9gt4tQPCBcyjSym4HUWz04FT03cfa6Yn7f2xHwjw7j
3DXGZnp qx3fox5ezwIDAQAB
-----END PUBLIC KEY-----
- kid: k2
privateKey: |
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIC3TBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQI1wWdC35fHiMCAggA
MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBAd8fAg67XxjRww/6zZOYiUBIIC
gFELh2xUblnGRTGQQ7VaRayAMHd1hNNc2P6Pe0ujXxzf1qF/iE6Rhpk2Ag9I0ky
siZL8lbh3QzOyQUch BBYBuUeToSN8jQ 5rTR6Vr8eaC6jRKIZCBdxbbN4r3hSkJ
nG BZzjArO8U0T QTQ3cp3rpvOsRj5xR09nku3HnaY9vNqWAWd5mjJ b8lRnh64X
qAAmnBUcB 5xuU/BMGpF8k2X/qNVRM/YZzP1/mWO31kw1VMjSP6zdrRVQuoUv2X8
bUlv6kBBaaTPK4qkR6y3I7QMiGxA1UL8qTPtxpOlaWR 3ofIBPk1N35k1sFLRgc/
UhK7q0KphZ75BZGu1PpOu8T0p2fx0BnIQUZKg7 g5oqzNKKo9RlVx87wrJO1Urur
VJS FzK1HoGqLtsco2lzeLqmXqCbC MXygenJJxOZOKW9/LWQxMP3e72/N0LzdqW
0lbu9f4w86OL7Qwk4zVxCFp9bwDAvf5ZIvfnLI1yl8q7cfny6QKGU nwENOWn3Px
I7Dv7vNIs9K6f6Is8XPnEnBIRi2eUwVHVqeu46DLIIzS YIvsDfkHp3h7fBh7hYW
Iytia/QfKmpyeZp5GCJZM0pLP0qDLspXdm0oBI WdnbF2i9YUADGdQw1CpgskYnf
wBNDdGmkUR4aTDdwvdzPacRDF3ZZg/AaiysuWRIjEsEabwmpi4CmmLiwxwnO5uDn
4iiLC16PUPK sIBskYd9UgOMyC qKbzajCZVyRZDpNPZF jZE ND3TOtaWHimP9M
B3dHj6F /rHHko9kWsc/V/RaXm//14g8SBn3Hc vR/IAz9SajMJDRVEmQnWhkfz9
IdLgYdHew8l HuvjDCXtmBM=
-----END ENCRYPTED PRIVATE KEY-----
publicKey: |
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6pyUTOpppX5JOtMbT6CjS4/U8
ZV6Tw9kViTwrILr/AwgUFaaxptaCx waiQgBJfTE2VVXwXipXpxfYjtgKqVqx4yR
mVAxGNZjKIkSzAxjo7er2vP084WG/Sh958MXW8A/K7pDkSALusP8YTveEgtTKfln
feBZh04XQmRYhPPCuQIDAQAB
-----END PUBLIC KEY-----
以上配置的私钥是进行了转换后的密钥
4. 解析配置文件
定义配置对象
代码语言:javascript复制package com.olive.jwt;
import java.io.Serializable;
public class CertVO implements Serializable{
private String kid;
private String privateKey;
private String publicKey;
//省略getter setter
}
对应配置对象
代码语言:javascript复制package com.olive.jwt;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix="certificate")
public class CertificateConfig {
private String useKid;
private List<CertVO> certificates;
//省略getter setter
}
5. 定义 jwt payload 对象
代码语言:javascript复制package com.olive.jwt;
import java.io.Serializable;
public class JwtPayloadVO implements Serializable{
private String jti; //jwt token id
private String tid; //companyId
private String cid; //appId
private String iss; //token使用方
private String sub; //token主题 格式 tid:cid:uid
private Long exp; //过期时间 毫秒
private Long iat; //创建时间 毫秒
private String uid; //user id
//省略getter setter
}
6. 进行jwt 生成与验证
代码语言:javascript复制package com.olive.jwt;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.text.ParseException;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import com.nimbusds.jose.*;
@Component
public class CertificateProvider {
@Autowired
private CertificateConfig certificateConfig;
private RSAPrivateKey rsaPrivateKey = null;
private Map<String, RSASSAVerifier> verifiers = new HashMap<>();
@PostConstruct
public void init() {
rsaPrivateKey = this.getPrivateKey();
}
public RSAPrivateKey getPrivateKey() {
if (rsaPrivateKey != null) {
return rsaPrivateKey;
}
String use = certificateConfig.getUseKid();
if (StringUtils.isEmpty(use)) {
System.out.println("certificate kid is required");
return null;
}
List<CertVO> certVOs = certificateConfig.getCertificates();
if (certVOs==null && certVOs.size()==0) {
System.out.println("certificate is required");
return null;
}
try {
for (CertVO certVO : certVOs) {
if (use.equals(certVO.getKid())) {
// 加载私钥
rsaPrivateKey = this.loadRSARSAPrivateKey(certVO.getPrivateKey());
}
RSAPublicKey publicKey = loadRSAPublicKey(certVO.getPublicKey());
verifiers.put(certVO.getKid(), new RSASSAVerifier(publicKey));
}
} catch (Exception e) {
e.printStackTrace();
}
if (rsaPrivateKey != null) {
return rsaPrivateKey;
} else {
System.out.println("getPrivateKey certificate kid is required,certificate kid is required");
return null;
}
}
/**
* 加载公钥
*
* @param keyStr 公钥字符串
* @return 公钥实体
* @throws NoSuchAlgorithmException KeyFactory中无该算法实现
* @throws InvalidKeySpecException 密钥无法识别
*/
private RSAPublicKey loadRSAPublicKey(String keyStr) throws NoSuchAlgorithmException, InvalidKeySpecException {
byte[] clear = publicKeyStrToBytes(keyStr);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(clear);
KeyFactory fact = KeyFactory.getInstance("RSA");
return (RSAPublicKey) fact.generatePublic(keySpec);
}
private RSAPrivateKey loadRSARSAPrivateKey(String keyStr) throws Exception {
String begin = "-----BEGIN PRIVATE KEY-----";
String end = "-----END PRIVATE KEY-----";
String key = keyStr.replace(begin, "").replace(end, "").replaceAll("\s", "");
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key));
KeyFactory kf = KeyFactory.getInstance("RSA");
return (RSAPrivateKey) kf.generatePrivate(spec);
}
/**
* 公钥 字符串转换成二进制
* @param keyStr 密钥字符串
* @return 密钥/公钥 二进制
*/
private byte[] publicKeyStrToBytes(String keyStr) {
String begin = "-----BEGIN PUBLIC KEY-----";
String end = "-----END PUBLIC KEY-----";
String key = keyStr.replace(begin, "").replace(end, "").replaceAll("\s", "");
return Base64.getDecoder().decode(key);
}
public String generateAccessToken(JwtPayloadVO jwtPayloadVO) {
Map<String, Object> playloadMap = new HashMap<>();
playloadMap.put("jti", jwtPayloadVO.getJti());
playloadMap.put("tid", jwtPayloadVO.getTid());
playloadMap.put("cid", jwtPayloadVO.getCid());
playloadMap.put("iss", jwtPayloadVO.getIss());
playloadMap.put("sub", jwtPayloadVO.getSub());
playloadMap.put("exp", jwtPayloadVO.getExp());
playloadMap.put("iat", jwtPayloadVO.getIat());
if (StringUtils.hasLength(jwtPayloadVO.getUid())) {
playloadMap.put("uid", jwtPayloadVO.getUid());
}
try {
// 创建JWS头,设置签名算法和类型
JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS256).keyID(certificateConfig.getUseKid())
.type(JOSEObjectType.JWT).build();
JWTClaimsSet claimsSet = JWTClaimsSet.parse(playloadMap);
// 创建RSA签名器
JWSSigner signer = new RSASSASigner(rsaPrivateKey, true);
SignedJWT signedJWT = new SignedJWT(header, claimsSet);
signedJWT.sign(signer);
return signedJWT.serialize();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public JWTClaimsSet verify(String token) throws ParseException, JOSEException {
SignedJWT jwt = SignedJWT.parse(token);
JWSHeader jwtHeader = jwt.getHeader();
String keyID = jwtHeader.getKeyID();
RSASSAVerifier verifier = verifiers.get(keyID);
if (verifier == null) {
System.out.println("jwt verify error: kid " keyID " mismatch RSASSAVerifier");
return null;
}
boolean verify = jwt.verify(verifier);
if (!verify) {
System.out.println("jwt verify fail, invalid token: " token);
return null;
}
return jwt.getJWTClaimsSet();
}
}