手记

springboot+jwt做api的token认证

本篇和大家分享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 return Jwts.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 static boolean isExpiration(String token, String encryKey) {2     try {3         return getClaimsBody(token, encryKey)4                 .getExpiration()5                 .before(new Date());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 return Jwts.parser()2         .setSigningKey(encryKey)3         .parseClaimsJws(token)4         .getBody();

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

因此通常会在存储的时候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 static Object getVal(String token, String encryKey, String key) {10     return getJws(token, encryKey).getBody().get(key);11 }12 13 /**14  * 获取body某个值,json字符转实体15  *16  * @param token17  * @param encryKey18  * @param key19  * @param tClass20  * @param <T>21  * @return22  */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         return JSON.parseObject(strJson, tClass);27     } catch (Exception ex) {28         return null;29     }30 }

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

  1 public class JwtUtil {  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, int secondTimeOut) { 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, int secondTimeOut) { 25  26         if (isJsonMpas) { 27             claimMaps.forEach((key, val) -> { 28                 claimMaps.put(key, JSON.toJSONString(val)); 29             }); 30         } 31         long currentTime = System.currentTimeMillis(); 32         return Jwts.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         return Jwts.parser() 55                 .setSigningKey(encryKey) 56                 .parseClaimsJws(token); 57     } 58  59     public static String getSignature(String token, String encryKey) { 60         try { 61             return getJws(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 static JwsHeader getHeader(String token, String encryKey) { 75         try { 76             return getJws(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 static Claims getClaimsBody(String token, String encryKey) { 90         return getJws(token, encryKey).getBody(); 91     } 92  93     /** 94      * 获取body某个值 95      * 96      * @param token 97      * @param encryKey 98      * @param key 99      * @return100      */101     public static Object getVal(String token, String encryKey, String key) {102         return getJws(token, encryKey).getBody().get(key);103     }104 105     /**106      * 获取body某个值,json字符转实体107      *108      * @param token109      * @param encryKey110      * @param key111      * @param tClass112      * @param <T>113      * @return114      */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             return JSON.parseObject(strJson, tClass);119         } catch (Exception ex) {120             return null;121         }122     }123 124     /**125      * 是否过期126      *127      * @param token128      * @param encryKey129      * @return130      */131     public static boolean isExpiration(String token, String encryKey) {132         try {133             return getClaimsBody(token, encryKey)134                     .getExpiration()135                     .before(new Date());136         } catch (ExpiredJwtException ex) {137             return true;138         }139     }140 141     public static String getSubject(String token, String encryKey) {142         try {143             return getClaimsBody(token, encryKey).getSubject();144         } catch (Exception ex) {145             return "";146         }147     }148 }

View Code

过滤器验证token

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

 1 public class AuthenFilter implements Filter { 2     @Override 3     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 4  5         HttpServletRequest rq = (HttpServletRequest) servletRequest; 6         HttpServletResponse rp = (HttpServletResponse) servletResponse; 7         RpBase rpBase = new RpBase(); 8         try { 9             //只接受post10             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 class WebFilterConfig { 3  4     @Bean 5     public FilterRegistrationBean setFilter() { 6  7         FilterRegistrationBean registrationBean = new FilterRegistrationBean(); 8         registrationBean.setFilter(new AuthenFilter()); 9         registrationBean.addUrlPatterns("/api/*");10         registrationBean.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);11 12         return registrationBean;13     }14 }

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

 1 @RestController 2 public class TestController { 3  4     @PostMapping("/api/t0") 5     public String t0() throws MyException { 6  7         return UUID.randomUUID().toString(); 8     } 9 10     @GetMapping("/token/{userName}")11     public String getToken(@PathVariable String userName) {12 13         MoUser moUser = new MoUser();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         return JwtUtil.getTokenByJson(map,21                 WebConfig.Token_EncryKey,22                 WebConfig.Token_SecondTimeOut);23     }24 }

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

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

 

git地址:    https://github.com/shenniubuxing3 

原文出处:https://www.cnblogs.com/wangrudong003/p/10122706.html  

0人推荐
随时随地看视频
慕课网APP