springboot+jwt做api的token认证

摘要:
本篇和大家分享jwt的使用,她主要用来生成接口访问的token和验证,其单独结合springboot来开发api接口token验证很是方便,由于jwt的token中存储有用户的信息并且有加密,所以适用于分布式,这样直接吧信息存储在用户本地减速了服务端存储sessiion或token的压力;如下快速使用:1˂!

本篇和大家分享jwt(json web token)的使用,她主要用来生成接口访问的token和验证,其单独结合springboot来开发api接口token验证很是方便,由于jwt的token中存储有用户的信息并且有加密,所以适用于分布式,这样直接吧信息存储在用户本地减速了服务端存储sessiion或token的压力;如下快速使用:

1 <!--jwt-->
2 <dependency>
3     <groupId>io.jsonwebtoken</groupId>
4     <artifactId>jjwt</artifactId>
5     <version>0.9.0</version>
6 </dependency>
7 <!--阿里 FastJson依赖-->
8 <dependency>
9     <groupId>com.alibaba</groupId>
10     <artifactId>fastjson</artifactId>
11     <version>1.2.44</version>
12 </dependency>

一般使用jwt来达到3种结果:

  • 生成token
  • 验证token是否有效
  • 获取token中jwt信息(主要用户信息)
生成token

引入了jjwt依赖后,要生成token很方便;对于一个token来说,代表的是唯一并且不可逆的,因此我们在生成时需要增加一些唯一数据进去,比如下面的id:

1 long currentTime =System.currentTimeMillis();
2 returnJwts.builder()
3 .setId(UUID.randomUUID().toString())
4         .setIssuedAt(new Date(currentTime))  //签发时间
5         .setSubject("system")  //说明
6         .setIssuer("shenniu003") //签发者信息
7         .setAudience("custom")  //接收用户
8         .compressWith(CompressionCodecs.GZIP)  //数据压缩方式
9 
10         .signWith(SignatureAlgorithm.HS256, encryKey) //加密方式
11         .setExpiration(new Date(currentTime + secondTimeOut * 1000))  //过期时间戳
12         .addClaims(claimMaps) //cla信息
13         .compact();

通过uuid来标记唯一id信息;当然在对token加密时需要用到秘钥,jwt很是方便她支持了很多中加密方式如:HS256,HS265,Md5等复杂及常用的加密方式;

jwt生成的token中内容分为3个部分:head信息,payload信息,sign信息,通常我们要做的是往payload增加一些用户信息(比如:账号,昵称,权限等,但不包含密码);在对jwt的token有一定了解后,我们来看下真实生成的token值:

1 eyJhbGciOiJIUzI1NiIsInppcCI6IkdaSVAifQ.H4sIAAAAAAAAAFWMTQ7CIBSE7_LWkPDzaEsP4QnYINCIptX4INE0vbtg4sLlfPPN7HAtGWbwg1BKL4GrcbEcIwpujZF8iiEpjXFapAAG2ReYpUEcR2VxYED13Nb0ppLW3hP1eEnblqsQuiFfY0OhUrl3I70evweU_aFSejZhd7DlcDv5NTmYHUilHTD3rf_hAccHRTv--7YAAAA.i4xwoQtaWI0-dwHWN8uZ4DBm-vfli5bavYU9lRYxU5E
验证token是否有效

token生成的时都会伴随者有一个失效的时间,在这我们可以通过setExpiration函数设置过期时间,记住jwt的有效时间不是滑动的,也就是说不做任何处理时,当到达第一次设置的失效时间时,就基本没用了,要获取token是否过期可以使用如下方式:

1 public staticboolean isExpiration(String token, String encryKey) {
2     try{
3         returngetClaimsBody(token, encryKey)
4 .getExpiration()
5                 .before(newDate());
6     } catch(ExpiredJwtException ex) {
7         return true;
8 }
9 }

这里使用了date的before来用获取的过期时间和当前时间对比,判断是否继续有效,需要注意的是如果在token失效后再通过getClaimsBody(token, encryKey)获取信息,此时会报ExpiredJwtException错误,我们即可认为过期。

获取token中jwt信息(主要用户信息)

通常我们要把登录用户信息存储在jwt生成的token中,这里可以通过addClaims(claimMaps)传递map来设置信息,反过来要获取token中的用户信息,我们需要这样做:

1 returnJwts.parser()
2 .setSigningKey(encryKey)
3 .parseClaimsJws(token)
4         .getBody();

此时body获取出来是Claims类型,我们需要从中获取到用户信息,需要注意的是在addClaims存储信息的时候如果存储的map值没做过出来,那完整的实体对象存储进去后会映射成一个LinkHasMap类型,如下:

springboot+jwt做api的token认证第1张

因此通常会在存储的时候json化,如下代码:

1 claimMaps.forEach((key, val) ->{
2 claimMaps.put(key, JSON.toJSONString(val));
3 });

再来就是通过get方法获取我们存储进去的信息,并json反序列化:

1 /**
2 * 获取body某个值
3 *
4 * @param token
5 * @param encryKey
6 * @param key
7 * @return
8 */
9 public staticObject getVal(String token, String encryKey, String key) {
10     return getJws(token, encryKey).getBody().get(key);
11 }
12 
13 /**
14 * 获取body某个值,json字符转实体
15 *
16 * @param token
17 * @param encryKey
18 * @param key
19 * @param tClass
20 * @param <T>
21 * @return
22  */
23 public static <T> T getValByT(String token, String encryKey, String key, Class<T>tClass) {
24     try{
25         String strJson =getVal(token, encryKey, key).toString();
26         returnJSON.parseObject(strJson, tClass);
27     } catch(Exception ex) {
28         return null;
29 }
30 }

来到这里一个Jwt的Util代码基本就完成了,下面给出完整的代码例子,仅供参考:

1 public classJwtUtil {
2 
3     /**
4 * 获取token - json化 map信息
5 *
6 * @param claimMaps
7 * @param encryKey
8 * @param secondTimeOut
9 * @return
10      */
11     public static String getTokenByJson(Map<String, Object> claimMaps, String encryKey, intsecondTimeOut) {
12         return getToken(claimMaps, true, encryKey, secondTimeOut);
13 }
14 
15     /**
16 * 获取token
17 *
18 * @param claimMaps
19 * @param isJsonMpas
20 * @param encryKey
21 * @param secondTimeOut
22 * @return
23      */
24     public static String getToken(Map<String, Object> claimMaps, boolean isJsonMpas, String encryKey, intsecondTimeOut) {
25 
26         if(isJsonMpas) {
27             claimMaps.forEach((key, val) ->{
28 claimMaps.put(key, JSON.toJSONString(val));
29 });
30 }
31         long currentTime =System.currentTimeMillis();
32         returnJwts.builder()
33 .setId(UUID.randomUUID().toString())
34                 .setIssuedAt(new Date(currentTime))  //签发时间
35                 .setSubject("system")  //说明
36                 .setIssuer("shenniu003") //签发者信息
37                 .setAudience("custom")  //接收用户
38                 .compressWith(CompressionCodecs.GZIP)  //数据压缩方式
39 
40                 .signWith(SignatureAlgorithm.HS256, encryKey) //加密方式
41                 .setExpiration(new Date(currentTime + secondTimeOut * 1000))  //过期时间戳
42                 .addClaims(claimMaps) //cla信息
43 .compact();
44 }
45 
46     /**
47 * 获取token中的claims信息
48 *
49 * @param token
50 * @param encryKey
51 * @return
52      */
53     private static Jws<Claims>getJws(String token, String encryKey) {
54         returnJwts.parser()
55 .setSigningKey(encryKey)
56 .parseClaimsJws(token);
57 }
58 
59     public staticString getSignature(String token, String encryKey) {
60         try{
61             returngetJws(token, encryKey).getSignature();
62         } catch(Exception ex) {
63             return "";
64 }
65 }
66 
67     /**
68 * 获取token中head信息
69 *
70 * @param token
71 * @param encryKey
72 * @return
73      */
74     public staticJwsHeader getHeader(String token, String encryKey) {
75         try{
76             returngetJws(token, encryKey).getHeader();
77         } catch(Exception ex) {
78             return null;
79 }
80 }
81 
82     /**
83 * 获取payload body信息
84 *
85 * @param token
86 * @param encryKey
87 * @return
88      */
89     public staticClaims getClaimsBody(String token, String encryKey) {
90         returngetJws(token, encryKey).getBody();
91 }
92 
93     /**
94 * 获取body某个值
95 *
96 * @param token
97 * @param encryKey
98 * @param key
99 * @return
100      */
101     public staticObject getVal(String token, String encryKey, String key) {
102         return getJws(token, encryKey).getBody().get(key);
103 }
104 
105     /**
106 * 获取body某个值,json字符转实体
107 *
108 * @param token
109 * @param encryKey
110 * @param key
111 * @param tClass
112 * @param <T>
113 * @return
114      */
115     public static <T> T getValByT(String token, String encryKey, String key, Class<T>tClass) {
116         try{
117             String strJson =getVal(token, encryKey, key).toString();
118             returnJSON.parseObject(strJson, tClass);
119         } catch(Exception ex) {
120             return null;
121 }
122 }
123 
124     /**
125 * 是否过期
126 *
127 * @param token
128 * @param encryKey
129 * @return
130      */
131     public staticboolean isExpiration(String token, String encryKey) {
132         try{
133             returngetClaimsBody(token, encryKey)
134 .getExpiration()
135                     .before(newDate());
136         } catch(ExpiredJwtException ex) {
137             return true;
138 }
139 }
140 
141     public staticString getSubject(String token, String encryKey) {
142         try{
143             returngetClaimsBody(token, encryKey).getSubject();
144         } catch(Exception ex) {
145             return "";
146 }
147 }
148 }
View Code
过滤器验证token

有了基本的JwtUtil工具,我们需要用到springboot项目中,一般来说对于登录授权token验证可以通过过滤器来操作,这里创建一个AuthenFilter,用于对post请求过来的token做验证:

1 public classAuthenFilter implements Filter {
2 @Override
3     public voiddoFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
4 
5         HttpServletRequest rq =(HttpServletRequest) servletRequest;
6         HttpServletResponse rp =(HttpServletResponse) servletResponse;
7         RpBase rpBase = newRpBase();
8         try{
9             //只接受post
10             if (!rq.getMethod().equalsIgnoreCase("post")) {
11 filterChain.doFilter(servletRequest, servletResponse);
12                 return;
13 }
14 
15             String token = rq.getHeader("token");
16             if(StringUtils.isEmpty(token)) {
17                 rpBase.setMsg("无token");
18                 return;
19 }
20 
21             //jwt验证
22             MoUser moUser = JwtUtil.getValByT(token, WebConfig.Token_EncryKey, WebConfig.Login_User, MoUser.class);
23             if (moUser == null) {
24                 rpBase.setMsg("token已失效");
25                 return;
26 }
27 
28             System.out.println("token用户:" +moUser.getNickName());
29 
30 filterChain.doFilter(servletRequest, servletResponse);
31         } catch(Exception ex) {
32         } finally{
33             if (!StringUtils.isEmpty(rpBase.getMsg())) {
34                 rp.setCharacterEncoding("utf-8");
35 rpBase.setCode(HttpStatus.BAD_REQUEST.value());
36 rp.getWriter().write(JSON.toJSONString(rpBase));
37 }
38 }
39 }
40 }

要是自定义过滤器AuthenFilter生效,还需要把她注册到容器中,这里通过编码方式,当然还可以通过@WebFilter注解来加入到容器中:

1 @Configuration
2 public classWebFilterConfig {
3 
4 @Bean
5     publicFilterRegistrationBean setFilter() {
6 
7         FilterRegistrationBean registrationBean = newFilterRegistrationBean();
8         registrationBean.setFilter(newAuthenFilter());
9         registrationBean.addUrlPatterns("/api/*");
10 registrationBean.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);
11 
12         returnregistrationBean;
13 }
14 }

注意addUrlPatterns匹配的是过滤器作用的url连接,根据需求而定;为了验证效果,这里我创建了两个接口getToken和t0,分别是获取token和post查询接口,代码如是:

1 @RestController
2 public classTestController {
3 
4     @PostMapping("/api/t0")
5     publicString t0() throws MyException {
6 
7         returnUUID.randomUUID().toString();
8 }
9 
10     @GetMapping("/token/{userName}")
11     publicString getToken(@PathVariable String userName) {
12 
13         MoUser moUser = newMoUser();
14 moUser.setUserName(userName);
15 moUser.setNickName(userName);
16 
17         Map<String, Object> map = new HashMap<>();
18 map.put(WebConfig.Login_User, moUser);
19 
20         returnJwtUtil.getTokenByJson(map,
21 WebConfig.Token_EncryKey,
22 WebConfig.Token_SecondTimeOut);
23 }
24 }

最终要获通过head传递token值来访问t01接口,得到如下结果:

springboot+jwt做api的token认证第2张

token在有效时间后访问直接失败,从新获取token并访问t01接口,得到成功的信息:

springboot+jwt做api的token认证第3张

免责声明:文章转载自《springboot+jwt做api的token认证》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇golang gin 返回json一行css代码搞定响应式布局下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Android进入一个新页面,EditText失去焦点并禁止弹出键盘

android在进入一个新页面后,edittext会自动获取焦点并弹出软键盘,这样并不符合用户操作习惯。 在其父控件下,添加如下的属性,就可以完美解决,使其进入页面后不主动获取焦点,并且不弹出软键盘: android:focusable="true"   android:focusableInTouchMode="true" 代码如下: 1 <S...

RabbitMQ 消息应答机制

一、概述 消费者处理一个任务是需要一段时间的,如果有一个消费者正在处理一个比较耗时的任务并且只处理了一部分,突然这个时候消费者宕机了,那么会出现什么情况呢? 要回答这个问题,我们先了解一下 RabbitMQ 的消息应答机制 为了保证消息从队列可靠地达到消费者并且被消费者消费处理,RabbitMQ 提供了消息应答机制,RabbitMQ 有两种应答机制,自动应...

DATASNAP REST WEBSERVICES中间件如何跨平台使用

准备使用DELPHI开发移动设备开发的朋友对DATASNAP REST中间件不可不了解。 DATASNAP REST新型WEBSERVICES中间件使用的通信协议和数据封装格式: 使用HTTP通信协议,HTTP协议哪个平台都支持;使用JSON作为数据的封装格式,几乎所有的开发语言都可以解析JSON数据。 REST的目的就是通过简单的URL来完成对中间层远程...

Scala入门系列(十二):隐式转换

引言 Scala提供的隐式转换和隐式参数功能,是非常有特色的功能。是Java等编程语言所没有的功能。它可以允许你手动指定,将某种类型的对象转换成其他类型的对象。通过这些功能可以实现非常强大而且特殊的功能。 Scala的隐式转换,其实最核心的就是定义隐式转换函数,即implicit conversion function。定义的隐式转换函数,只要在编写的...

黄聪:PHP字符串操作(string替换、删除、截取、复制、连接、比较、查找、包含、大小写转换、切割成数组等)

一、字符串替换 str_replace("iwind", "kiki", "i love iwind, iwind said"); 将输出 "i love kiki, kiki said" str_replace(find,replace,string,count)参数 描述  find 必需。规定要查找的值。 replace 必需。规定替换 find...

Java泛型讲解

1. 概述在引入泛型之前,Java类型分为原始类型、复杂类型,其中复杂类型分为数组和类。引入范型后,一个复杂类型就可以在细分成更多的类型。例如原先的类型List,现在在细分成List<Object>, List<String>等更多的类型。注意,现在List<Object>, List<String>是两种不...