SpringBoot整合JWT

一、JWT是什么

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).定义了一种简洁的,自包含的方法用于通信双方之间以JSON对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。

1.1、JWT的执行流程

  1. 用户使用账号和面发出post请求;
  2. 服务器使用私钥创建一个jwt;
  3. 服务器返回这个jwt给浏览器;
  4. 浏览器将该jwt串在请求头中向服务器发送请求;
  5. 服务器验证该jwt;
  6. 返回响应的资源给浏览器

1.2、JWT的优点

1.简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快

2.自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库

3.因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持。

4.不需要在服务端保存会话信息,特别适用于分布式微服务。

1.3、JWT的结构

WT的头部承载两部分信息:token类型和采用的加密算法

1
2
3
4
{ 
"alg": "HS256",
"typ": "JWT"
}

Payload

载荷就是存放有效信息的地方

Signature

jwt的第三部分是一个签证信息,这个签证信息由三部分组成: header (base64后的) payload (base64后的) secret。

这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。
密钥secret是保存在服务端的,服务端会根据这个密钥进行生成token和进行验证,所以需要保护好

二、SpringBoot整合JWT

1、pom文件中增加如下依赖

1
2
3
4
5
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.5.0</version>
</dependency>

2、生成JWT

.withClaim()方法用于将一些非紧要的数据放进jwt

.sign()方法需指定生成jwt的算法及加密盐

1
2
3
String token = JWT.create().withClaim("userid",21)
.withClaim("username","admin")
.sign(Algorithm.HMAC256("sfsfsfs"));

在请求中将token带给前端

我们可以看到token数据就已经带到前端了

3、获取token

注意:

Algorithm.HMAC256(“sfsfsfs”)方法里是你前面生成token定义的盐,这里要一样;

jwtVerifier.verify()方法里的参数是你生成的token

1
2
3
4
5
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("sfsfsfs"))
.build();
DecodedJWT verify = jwtVerifier.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyaWQiOjIxLCJ1c2VybmFtZSI6ImFkbWluIn0.506Vfj6fiPyONTLQ-QNOyiMQo_eyfcxD3viUrUMDACQ");
System.out.println(verify.getClaim("userid").asInt());
System.out.println(verify.getClaim("username").asString());

可以看到,我们取出了用户信息

如何生成我们知道了,那就可以写一个工具类

3、编写工具类

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
private static final String SIGN = "$%egb***#@5";

/**
* 生成token
*/
public static String getToken(Map<String,String> map){
Calendar instance = Calendar.getInstance();
instance.add(Calendar.SECOND,60);
JWTCreator.Builder builder = JWT.create();
map.forEach((k,v)->{
builder.withClaim(k,v);
});
String token = builder.withExpiresAt(instance.getTime())
.sign(Algorithm.HMAC256(SIGN));
return token;
}

/**
* 验证token
* @param token
*/
public static void verify(String token){
JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);

}

/**
* 获取token信息
* @param token
* @return
*/
public static DecodedJWT getTokenInfo(String token){
DecodedJWT verify = JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
return verify;
}

到这,我们生成token时,只需要将用户map传入getToken()方法即可

4、验证token

1
2
3
4
5
6
7
8
9
10
try {
DecodedJWT verify = JWTUtils.verify(token);
map.put("msg","验证成功");
}catch (SignatureVerificationException e){
e.printStackTrace();
map.put("msg","无效签名");
}catch (TokenExpiredException e){
e.printStackTrace();
map.put("msg","token过期");
}

在开发中,有很多的接口,不可能每个接口都这样验证,所以得加一个拦截器

5、编写拦截器

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
public class JWTInterceptor implements HandlerInterceptor {


@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

String token = request.getHeader("token");
Map map = new HashMap();
try {
DecodedJWT verify = JWTUtils.verify(token);
return true;//放行
}catch (SignatureVerificationException e){
e.printStackTrace();
map.put("msg","无效签名");
}catch (TokenExpiredException e){
e.printStackTrace();
map.put("msg","token过期");
}
String json = new ObjectMapper().writeValueAsString(map);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(json);
response.getWriter().close();
return false;
}
}

配置拦截器

1
2
3
4
5
6
7
8
9
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new JWTInterceptor())
.addPathPatterns("/user/test")
.excludePathPatterns("/user/login");
}
}

这样,我们的业务代码中就不会每次要验证了

访问localhost:8080/user/login登录

然后再访问localhost:8080/user/test测试(在headers加入token)

如果token错误