zoukankan      html  css  js  c++  java
  • 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 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         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      * @return
    100      */
    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 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             return JSON.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 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 }
    复制代码

    过滤器验证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<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接口,得到成功的信息:

     https://www.cnblogs.com/wangrudong003/p/10122706.html

  • 相关阅读:
    “问答回复模块”Java开发文档官方改进版讲解【在线实习·吾研第二期】
    “付费邀请模块”产品原型图评审【在线实习·吾研第三期】
    “学长学姐认证模块”测试用例官方改进版讲解【在线实习·吾研第一期】
    “学长认证模块”Java代码2.0官方版要点讲解【在线实习·吾研第一期】
    “问答评论模块”UI作品评审【在线实习·吾研第二期】
    “认证模块”前端代码1.0评审【在线实习·吾研第一期】
    <<中国专利法详解>>学习笔记(一)
    JS 前端获得时间
    北漂的程序员
    Spring类注入异常
  • 原文地址:https://www.cnblogs.com/sjqq/p/10136048.html
Copyright © 2011-2022 走看看