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

  • 相关阅读:
    什么是webview
    juqery.fn.extend和jquery.extend
    LeetCode
    5. Longest Palindromic Substring
    42. Trapping Rain Water
    11. Container With Most Water
    621. Task Scheduler
    49. Group Anagrams
    739. Daily Temperatures
    3. Longest Substring Without Repeating Characters
  • 原文地址:https://www.cnblogs.com/sjqq/p/10136048.html
Copyright © 2011-2022 走看看