JWT发放和验证TOKEN

前言

之前我们从研究官方文档去学习 jjwt 的使用, 本文做一个jwt的完整使用jar

实现

maven依赖

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
<dependencies>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>

<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>

常量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Conf {

/**
* 系统环境变量key
*/
public static final String SYS_ENV = "IDEA360_HOME";

/**
* 默认配置文件名
*/
public static final String DEFAULT_CONF_NAME = "conf.properties";

/**
* 私钥约定配置文件key
*/
public static final String PRIVATEKEY_CONF = "idea360.auth.jwt.privatekey";

/**
* 公钥约定配置文件key
*/
public static final String PUBLICKEY_CONF = "idea360.auth.jwt.publicKey";
}

配置文件加载器

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
/**
* 加载指定路径系统环境变量
*/

public class ConfigLoader {

private static ConfigLoader instance = null;
private Properties globalConfigs = null;

public static ConfigLoader getInstance() {
if (instance == null) {
instance = new ConfigLoader();
}
return instance;
}

private ConfigLoader() {
this.init();
}

private void init() {
boolean hasConfig = this.checkHasConfig(System.getenv(Conf.SYS_ENV));
if (!hasConfig) {
throw new RuntimeException("没找到全局配置文件");
}
}

private boolean checkHasConfig(String baseEnv) {
String defaultPath = baseEnv + File.separator + "conf" + File.separator;
File file = new File(defaultPath);
if (file.exists() && file.isDirectory()) {
File conf = new File(defaultPath + Conf.DEFAULT_CONF_NAME);
if (conf.exists() && conf.isFile()) {
this.globalConfigs = new Properties();
try {
FileInputStream inputStream = new FileInputStream(conf);
this.globalConfigs.load(inputStream);
inputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
} else {
return false;
}
}

public String getProperty(String key) {
return this.globalConfigs.getProperty(key, null);
}
}

负载

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
public class Payload implements Serializable {

/**
* 公司id
*/
private Integer companyId;
/**
* 用户id
*/
private String userId;
/**
* appId
*/
private String appId;


public Payload(Integer companyId, String userId, String appId) {
this.companyId = companyId;
this.userId = userId;
this.appId = appId;
}

public Integer getCompanyId() {
return companyId;
}

public void setCompanyId(Integer companyId) {
this.companyId = companyId;
}

public String getUserId() {
return userId;
}

public void setUserId(String userId) {
this.userId = userId;
}

public String getAppId() {
return appId;
}

public void setAppId(String appId) {
this.appId = appId;
}

@Override
public String toString() {
return "Payload{" +
"companyId=" + companyId +
", userId='" + userId + '\'' +
", appId='" + appId + '\'' +
'}';
}

public static PayloadBuilder builder() {
return new PayloadBuilder();
}

public static class PayloadBuilder {
private Integer companyId;
private String userId;
private String appId;

public PayloadBuilder() {
}

public PayloadBuilder setCompanyId(Integer companyId) {
this.companyId = companyId;
return this;
}

public PayloadBuilder setUserId(String userId) {
this.userId = userId;
return this;
}

public PayloadBuilder setAppId(String appId) {
this.appId = appId;
return this;
}

public Payload build() {
return new Payload(this.companyId, this.userId, this.appId);
}

@Override
public String toString() {
return "PayloadBuilder{" +
"companyId=" + companyId +
", userId='" + userId + '\'' +
", appId='" + appId + '\'' +
'}';
}
}
}

Token工具

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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
public class JwtGenerator {


private static Logger log = LoggerFactory.getLogger(JwtGenerator.class);

// 默认2h token失效
private final Long DEFAULT_EXPIRE_TIME = 2L;
// 私钥路径
private String privateKey;
// 公钥路径
private String publicKey;

public JwtGenerator() {
this.loadPrivateKey();
}

/**
* 读取环境变量配置文件
*/
private void loadPrivateKey() {
String privatekeyProperty = ConfigLoader.getInstance().getProperty(Conf.PRIVATEKEY_CONF);
String publickeyProperty = ConfigLoader.getInstance().getProperty(Conf.PUBLICKEY_CONF);

// if (Objects.isNull(privatekeyProperty)) {
// throw new NullPointerException("找不到私钥配置" + Conf.PRIVATEKEY_CONF);
// }
// if (Objects.isNull(publickeyProperty)) {
// throw new NullPointerException("找不到公钥配置" + Conf.PUBLICKEY_CONF);
// }

this.privateKey = privatekeyProperty;
this.publicKey = publickeyProperty;
}

/**
* 生成token
* @param payload
* @return
* @throws InvalidKeySpecException
* @throws NoSuchAlgorithmException
*/
public String generateToken(Payload payload) throws InvalidKeySpecException, NoSuchAlgorithmException {
return this.generateToken(payload, DEFAULT_EXPIRE_TIME);
}

/**
* 生成token
* @param payload token负载
* @param expire token有效期,单位: 分钟
* @return
* @throws InvalidKeySpecException
* @throws NoSuchAlgorithmException
*/
public String generateToken(Payload payload, Long expire) throws InvalidKeySpecException, NoSuchAlgorithmException {
if (Objects.isNull(payload.getCompanyId()) || Objects.isNull(payload.getUserId())) {
throw new NullPointerException("companyId和userId不能为空");
}

if (Objects.isNull(privateKey) || "".equals(privateKey)) {
throw new NullPointerException("请检查私钥配置是否正确: " + Conf.PRIVATEKEY_CONF);
}

String resourceKey = readResourceKey(privateKey);
if (Objects.isNull(resourceKey)) {
throw new NullPointerException("请检查私钥文件是否正确");
}
PrivateKey privateKey = getPrivateKey(resourceKey);

expire = Objects.isNull(expire) || expire <= 0L? DEFAULT_EXPIRE_TIME : expire;

String token = Jwts.builder().setSubject("易聊科技")
.setIssuer("易聊科技")
.setIssuedAt(Date.from(Instant.now()))
.setExpiration(Date.from(Instant.now().plus(expire, ChronoUnit.MINUTES)))
.claim("companyId", payload.getCompanyId())
.claim("userId", payload.getUserId())
.claim("appId", payload.getAppId())
.signWith(SignatureAlgorithm.RS256, privateKey)
.setId(UUID.randomUUID().toString())
.compact();

log.info("Generator Token: [{}]", token);
return token;
}

/**
* token验证
* @param token
* @return
* @throws InvalidKeySpecException
* @throws NoSuchAlgorithmException
*/
public Payload verifyToken(String token) throws InvalidKeySpecException, NoSuchAlgorithmException {
if (Objects.isNull(token) || "".equals(token)) {
throw new NullPointerException("token不能为空");
}

if (Objects.isNull(publicKey) || "".equals(publicKey)) {
throw new NullPointerException("请检查公钥配置是否正确: " + Conf.PUBLICKEY_CONF);
}

String resourceKey = readResourceKey(publicKey);
if (Objects.isNull(resourceKey)) {
throw new NullPointerException("请检查公钥文件是否正确");
}
PublicKey publicKey = getPublicKey(resourceKey);

try {
Jws<Claims> jws = Jwts.parser()
.setSigningKey(publicKey)
.parseClaimsJws(token);

log.info("Header : [{}]", jws.getHeader());
log.info("Body : [{}]", jws.getBody());
log.info("Signature : [{}]", jws.getSignature());

Claims claims = jws.getBody();
String appId = claims.get("appId", String.class);
String userId = claims.get("userId", String.class);
Integer companyId = claims.get("companyId", Integer.class);
return Payload.builder().setCompanyId(companyId).setUserId(userId).setAppId(appId).build();

} catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | SignatureException | IllegalArgumentException e) {
log.error("token校验失败: " + e);
}
return null;
}

/**
* 生成私钥
* @param rsaPrivateKey
* @return
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
*/
private PrivateKey getPrivateKey(String rsaPrivateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
rsaPrivateKey = rsaPrivateKey.replace("-----BEGIN RSA PRIVATE KEY-----", "");
rsaPrivateKey = rsaPrivateKey.replace("-----END RSA PRIVATE KEY-----", "");

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

/**
* 生成公钥
* @param rsaPublicKey
* @return
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
*/
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");
return kf.generatePublic(keySpec);
}

/**
* 读取资源文件
* @param fileName
* @return
*/
private String readResourceKey(String fileName) {
String key = null;
try {
File file = new File(fileName);
if (file.exists() && file.isFile()) {
List<String> lines = FileUtils.readLines(file, Charset.defaultCharset());
lines = lines.subList(1, lines.size() - 1);
key = String.join("", lines);
}
} catch (IOException e) {
log.error("读取私钥失败", e);
}
return key;
}

}

最后

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