SpringBoot整合JWT
一、JWT是什么
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON
的开放标准((RFC 7519).定义了一种简洁的,自包含的方法用于通信双方之间以JSON对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。
1.1、JWT的执行流程
- 用户使用账号和面发出post请求;
- 服务器使用私钥创建一个jwt;
- 服务器返回这个jwt给浏览器;
- 浏览器将该jwt串在请求头中向服务器发送请求;
- 服务器验证该jwt;
- 返回响应的资源给浏览器
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错误