JJWT

概述

jjwt基本使用示例

读取公钥文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 读取公钥
*/
@Test
void pem() {
String resourceKey = readResourceKey("E:\\example\\spring-cloud-learning\\idc-sso\\public.pem");
System.out.println(resourceKey);
}

public String readResourceKey(String fileName) {
String key = null;
try {
File file = new File(fileName);
List<String> lines = FileUtils.readLines(file, Charset.defaultCharset());
lines = lines.subList(1, lines.size() - 1);
key = lines.stream().collect(Collectors.joining());
} catch (IOException e) {
e.printStackTrace();
}
return key;
}

JKS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**
* 参考:https://www.codeproject.com/Articles/1253786/Java-JWT-Token-Tutorial-using-JJWT-Library
*
* Create Java KeyStore:
*
* keytool -genkey -alias idea360 -keyalg RSA -keystore idea360.jks -keysize 2048
* 输入密钥库口令:
* 再次输入新口令:
* 您的名字与姓氏是什么?
* [Unknown]: cui
* 您的组织单位名称是什么?
* [Unknown]: easyliao
* 您的组织名称是什么?
* [Unknown]: idea360
* 您所在的城市或区域名称是什么?
* [Unknown]: bj
* 您所在的省/市/自治区名称是什么?
* [Unknown]: bj
* 该单位的双字母国家/地区代码是什么?
* [Unknown]: cn
* CN=cui, OU=easyliao, O=idea360, L=bj, ST=bj, C=cn是否正确?
* [否]: 是
*
* Export the Public Key/Certificate:
*
* keytool -export -keystore idea360.jks -alias idea360 -file idea360.cer
*
*
* @throws Exception
*/
@Test
void jks() throws Exception{
String jksPassword = "abc123";

KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(new FileInputStream("E:\\example\\spring-cloud-learning\\idc-sso\\idea360.jks"), jksPassword.toCharArray());
Key key = ks.getKey("idea360", jksPassword.toCharArray());

String encodedPublicKey = Base64.getEncoder().encodeToString(key.getEncoded());
System.out.println("jks:");
System.out.println(encodedPublicKey);
}

@Test
void cer() throws Exception{
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = cf.generateCertificate(new FileInputStream("E:\\example\\spring-cloud-learning\\idc-sso\\idea360.cer"));
PublicKey publicKey = cert.getPublicKey();
// 将公钥对象转为字符串
String encodedPublicKey = Base64.getEncoder().encodeToString(publicKey.getEncoded());
System.out.println("Public Key:");
System.out.println(encodedPublicKey);
}

PEM

生成私钥

PKCS1的文件头格式 -----BEGIN RSA PRIVATE KEY-----
PKCS8的文件头格式 -----BEGIN PRIVATE KEY-----

生成RSA私钥, size 1024, 并输出到 private.pem 文件

1
2
openssl genrsa -out rsa_private_key.pem 1024
openssl pkcs8 -topk8 -inform PEM -outform PEM -in rsa_private_key.pem -out private.pem -nocrypt

私钥内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAK1al6EzuB5AIbk3
gXF4XQ9SJfqyRC/A935E7Kt48teEzwarorVO7oy4Hww6Meyu0mIgLgtTckCeE50u
/S7G1ocCYwyUXoet2leEXMLgePsUnB75JjGGCgwl2e+GG1SaCmmrHBURd6tMzsJk
dDTgE7yux8xQKk8TY+wCTaI+on1zAgMBAAECgYA11L7nOQdApYuTV3Ajy0uimVDf
44GXDqYMGN0hdkJ6uGwNVzkEcklTe01w35VnOPZtsC58J+J/vFa85b2LTAWLjlDY
/T3AulYDmT3ihPKIU401uRriMluIipyL7HNppxy2E85iRMfOiPnZgzF1EvCG36Uu
dHfyEtPLRYz1Y4FnwQJBANY3wCaxffN+rv11o+lSU5Dca8dOjwZPI7vF5kILMkAL
wZffZkpRIPTAg22lT3PwVov93+PHTPHWAleBsas5ghMCQQDPKnB3gOkSNvPy5uw1
2GIGoQdB/IaezeDtivDjKTQLPoGgYgEembY/i+TO5QhDDAQ6w5uEpaVzO6Sw3phz
04MhAkAGBi1WKTmkhlR/djZ00kbdVQWG5lOpVsJnfmo6QzxXhr0b+0FSJ7Pojh1k
EPkKBqE4cJxCOJGWX3FTaqdHbGeXAkBhiMKQkOGqkLLDzpKxOyHTqyamm4Sp6ZYX
YE3PNUvqa1BOOTX7uTTDsFJWUiBUqKipHgTvK6m8mf0MxJAUICXhAkEA1NOaPevO
qoTLx+gdiIG2fNmaC/aT9tWr87jqigKkLNee2NEsDrWxaEAVJSQ0F5fpZtcqvvu7
5TyPm1Ca649LUw==
-----END PRIVATE KEY-----

生成公钥

1
openssl rsa -in rsa_private_key.pem  -outform PEM -pubout -out public.pem

公钥内容

1
2
3
4
5
6
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCtWpehM7geQCG5N4FxeF0PUiX6
skQvwPd+ROyrePLXhM8Gq6K1Tu6MuB8MOjHsrtJiIC4LU3JAnhOdLv0uxtaHAmMM
lF6HrdpXhFzC4Hj7FJwe+SYxhgoMJdnvhhtUmgppqxwVEXerTM7CZHQ04BO8rsfM
UCpPE2PsAk2iPqJ9cwIDAQAB
-----END PUBLIC KEY-----

示例一

maven依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>

Java示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package cn.idea360.idc;

import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

import io.jsonwebtoken.security.Keys;

@Slf4j
@SpringBootTest
class IdcSsoApplicationTests {

/**
* @author 当我遇上你
* @email idea360@foxmail.com
* @公众号 当我遇上你
*
* 参考
* https://github.com/jwtk/jjwt
* https://www.viralpatel.net/java-create-validate-jwt-token/
* http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html
* http://andaily.com/blog/?p=956
*
* 日志
* 2020-08-26 18:32:30.863 INFO 14272 --- [ main] cn.idea360.idc.IdcSsoApplicationTests : token:[eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiLlvZPmiJHpgYfkuIrkvaAiLCJzdWIiOiJpZGVhMzYwIiwiYXVkIjoiYXBwIiwiaWF0IjoxNTk4NDM3OTUwLCJleHAiOjE1OTg0MzgwNzAsIm5iZiI6MTU5ODQzNzk1MCwidXNlcklkIjoiYWRtaW4iLCJjb21wYW55SWQiOjEsImp0aSI6ImFlZjJmOWY0LTVhMjYtNGI2ZS05YjRjLThjODE4MDMzYWVkNiJ9.Tpixe02lbHajDHoFvVMeSLneaQpDXgovu6K0CGzdLHZU5haS7k77WK6pKZ_LqAa3QOJ1ICFat3p5SDwtW22kmp0ATMLV33M96359ds6yD1nPPXs10Hurdzo83P1OmrMgzZXuskvc1w6EmfUw0vL6XSPdlGRQqgAhQerRUTJYlPvREoFH3G1AxZEhljpLkwfLX6VNpif3h4_4TKiEu9TMlP4Ol1Iou87nGqJA0YKIpvT2fAZDH4RjBqoi7BsRvmo6MYsmSkfpYy6cONVY_VhJ2HqLclEvWgZX_sNmTeGfgxNd0D_g7J3jduqTo0mkeO50_9gmmvF6w9yt_D10G1tFDw]
* 2020-08-26 18:32:30.902 INFO 14272 --- [ main] cn.idea360.idc.IdcSsoApplicationTests : id:[aef2f9f4-5a26-4b6e-9b4c-8c818033aed6]
* 2020-08-26 18:32:30.902 INFO 14272 --- [ main] cn.idea360.idc.IdcSsoApplicationTests : subject:[idea360]
* 2020-08-26 18:32:30.902 INFO 14272 --- [ main] cn.idea360.idc.IdcSsoApplicationTests : userId:[admin]
* 2020-08-26 18:32:30.902 INFO 14272 --- [ main] cn.idea360.idc.IdcSsoApplicationTests : companyId:[1]
* 2020-08-26 18:32:30.902 INFO 14272 --- [ main] cn.idea360.idc.IdcSsoApplicationTests : 签发者:[当我遇上你]
* 2020-08-26 18:32:30.902 INFO 14272 --- [ main] cn.idea360.idc.IdcSsoApplicationTests : 签发时间:[2020‐08‐26 18:32:30]
* 2020-08-26 18:32:30.903 INFO 14272 --- [ main] cn.idea360.idc.IdcSsoApplicationTests : 过期时间:[2020‐08‐26 18:34:30]
*/
@Test
void createJwt() {

// 非对称秘钥
KeyPair keyPair = Keys.keyPairFor(SignatureAlgorithm.RS256); //or RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();

String encodedPublicKey = Base64.getEncoder().encodeToString(publicKey.getEncoded());
String encodedPrivateKey = Base64.getEncoder().encodeToString(privateKey.getEncoded());

log.info("公钥:\n {}", convertToPublicKey(encodedPublicKey));
log.info("私钥:\n {}", convertToPrivateKey(encodedPrivateKey));


// 签名
String token = Jwts.builder()
.setIssuer("当我遇上你") // jwt签发人
.setSubject("idea360") // 代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可以存放什么userid,roldid之类的,作为什么用户的唯一标志。
.setAudience("app") // jwt的接受者
.setIssuedAt(new Date()) //jwt的签发时间
.signWith(privateKey) // 密钥
.setExpiration(new Date(System.currentTimeMillis() + 2 * 1000 * 60))// 设置过期时间
.setNotBefore(new Date()) //生效时间
.claim("userId", "admin") // 自定义属性
.claim("companyId", 1)
.setId(UUID.randomUUID().toString()) // 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
.compact();
log.info("token:[{}]", token);

// 验签
Jws<Claims> jws = null;
try {
jws = Jwts.parserBuilder()
.setSigningKey(publicKey)
.build()
.parseClaimsJws(token);
// we can safely trust the JWT
}catch (JwtException ex) {
ex.printStackTrace();
// we *cannot* use the JWT as intended by its creator
}
assert jws != null;
Claims claims = jws.getBody();

log.info("id:[{}]", claims.getId());
log.info("subject:[{}]", claims.getSubject());
log.info("userId:[{}]", claims.get("userId"));
log.info("companyId:[{}]", claims.get("companyId"));
log.info("签发者:[{}]", claims.getIssuer());

SimpleDateFormat sdf=new SimpleDateFormat("yyyy‐MM‐dd HH:mm:ss");
log.info("签发时间:[{}]", sdf.format(claims.getIssuedAt()));
log.info("过期时间:[{}]", sdf.format(claims.getExpiration()));
}

// Add BEGIN and END comments
private String convertToPublicKey(String key){
StringBuilder result = new StringBuilder();
result.append("-----BEGIN PUBLIC KEY-----\n");
result.append(key);
result.append("\n-----END PUBLIC KEY-----");
return result.toString();
}

// Add BEGIN and END comments
private String convertToPrivateKey(String key){
StringBuilder result = new StringBuilder();
result.append("-----BEGIN PRIVATE KEY-----\n");
result.append(key);
result.append("\n-----END PRIVATE KEY-----");
return result.toString();
}

}

示例二

maven依赖

1
2
3
4
5
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>

Java示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package cn.idea360.idc;


import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;

@Slf4j
@SpringBootTest
class IdcSsoApplicationTests {


/**
* @author 当我遇上你
* @email idea360@foxmail.com
* @公众号 当我遇上你
* Header : {alg=RS256}
* Body : {sub=adam, exp=1598447507, iss=info@wstutorial.com, groups=[user, admin]}
* Signature : FRRtPf6tTyr1FMYAA-vsmjSd58px1-staXWQlzwUOpeMxOn0o9vMAmcXkosvs7z-8oQ4ZlojdQkRwlm2QjGQGcU5l5dnlhYaaGhfAGUySvXL5YXcRwz5Amc9dGsA14G-JlD4b9zWo1euyKJN8g3nsZByx1a_w-iA_bLe81WCiMs
* @throws NoSuchAlgorithmException
*/
@Test
public void testJWTWithRsa() throws NoSuchAlgorithmException, InvalidKeySpecException {
KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("RSA");
keyGenerator.initialize(1024);

KeyPair kp = keyGenerator.genKeyPair();
PublicKey publicKey = kp.getPublic();
PrivateKey privateKey = kp.getPrivate();

// 将公钥对象转为字符串
String encodedPublicKey = Base64.getEncoder().encodeToString(publicKey.getEncoded());
System.out.println("Public Key:");
System.out.println(convertToPublicKey(encodedPublicKey));

// 将私钥对象转为字符串
String encodedPrivateKey = Base64.getEncoder().encodeToString(privateKey.getEncoded());
System.out.println("Private Key:");
System.out.println(convertToPrivateKey(encodedPrivateKey));

// 将字符串转为私钥对象
privateKey = getPrivateKey(encodedPrivateKey);

// 获取token
String token = generateJwtToken(privateKey);

// 将字符串类型转为公钥对象
publicKey = getPublicKey(encodedPublicKey);

// token解析
printStructure(token, publicKey);
}

@SuppressWarnings("deprecation")
public String generateJwtToken(PrivateKey privateKey) {
String token = Jwts.builder().setSubject("adam")
.setIssuedAt(Date.from(Instant.now()))
.setExpiration(Date.from(Instant.now().plus(5l, ChronoUnit.MINUTES)))
.setIssuer("info@wstutorial.com")
.claim("groups", new String[] { "user", "admin" })
.signWith(SignatureAlgorithm.RS256, privateKey) // RS256 with privateKey
.compact();
return token;
}


//Print structure of JWT
public void printStructure(String token, PublicKey publicKey) {
Jws parseClaimsJws = Jwts.parser()
.setSigningKey(publicKey)
.parseClaimsJws(token);
System.out.println("Header : " + parseClaimsJws.getHeader());
System.out.println("Body : " + parseClaimsJws.getBody());
System.out.println("Signature : " + parseClaimsJws.getSignature());
}


// Add BEGIN and END comments
private String convertToPublicKey(String key){
StringBuilder result = new StringBuilder();
result.append("-----BEGIN PUBLIC KEY-----\n");
result.append(key);
result.append("\n-----END PUBLIC KEY-----");
return result.toString();
}

// Add BEGIN and END comments
private String convertToPrivateKey(String key){
StringBuilder result = new StringBuilder();
result.append("-----BEGIN PRIVATE KEY-----\n");
result.append(key);
result.append("\n-----END PRIVATE KEY-----");
return result.toString();
}

// replace BEGIN and END comments
private PrivateKey getPrivateKey(String rsaPrivateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
rsaPrivateKey = rsaPrivateKey.replace("-----BEGIN PRIVATE KEY-----", "");
rsaPrivateKey = rsaPrivateKey.replace("-----END PRIVATE KEY-----", "");

PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(rsaPrivateKey));
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privateKey = kf.generatePrivate(keySpec);
return privateKey;
}

// replace BEGIN and END comments
private PublicKey getPublicKey(String rsaPublicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
rsaPublicKey = rsaPublicKey.replace("-----BEGIN PUBLIC KEY-----", "");
rsaPublicKey = rsaPublicKey.replace("-----END PUBLIC KEY-----", "");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(rsaPublicKey));
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey publicKey = kf.generatePublic(keySpec);
return publicKey;
}

}


最后

本文到此结束,感谢阅读。如果您觉得不错,请关注公众号【当我遇上你】,您的支持是我写作的最大动力。