springboot+jwt做api的token认证

本篇和大家分享jwt(json web token)的使用,她主要用来生成接口访问的token和验证,其单独结合springboot来开发api接口token验证很是方便,由于jwt的token中存储有用户的信息并且有加密,所以适用于分布式,这样直接吧信息存储在用户本地减速了服务端存储sessiion或token的压力;如下快速使用: 复制代码 1 2 3 io.jsonwebtoken 4 jjwt 5 0.9.0 6 7 8 9 com.alibaba 10 fastjson 11 1.2.44 12 复制代码 一般使用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 token 17 * @param encryKey 18 * @param key 19 * @param tClass 20 * @param 21 * @return 22 */ 23 public static T getValByT(String token, String encryKey, String key, Class 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代码基本就完成了,下面给出完整的代码例子,仅供参考: 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 //只接受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 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 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://yq.aliyun.com/u/shenniu003https://www.cnblogs.com/wangrudong003/p/10122706.html
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信