zoukankan      html  css  js  c++  java
  • Jwt拦截器和工具类

    JWT json web token

    Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

    综合了网上一些现有帖子写了一个工具类和拦截器

    工具类
     1 public class JWTUtil {
     2     //密匙
     3     private static final String TOKEN_SECRET = "privateKey";
     4 
     5     /**
     6      * 创建jwt
     7      * @param subject {id:100,name:xiaohong} 用户id,用户的name,用户的pasword都可以
     8      * @param liveMillis 存活时间
     9      * @return jwt
    10      */
    11 
    12     public static String setToken(String subject,
    13                                   long liveMillis ){
    14         //生成随机的32位的jwtId
    15         String jwtId= UUID.randomUUID().toString().replace("-", "").toUpperCase();
    16         //指定签名的时候使用的加密算法,就是header
    17         SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
    18         //设置签证颁发时间
    19         long nowMillis = System.currentTimeMillis();
    20         Date now = new Date(nowMillis);
    21         //创建payload的私有声明
    22         Map<String,Object> claims = new HashMap<String,Object>();
    23         claims.put("uid", "DSSFAWDWADAS...");
    24         claims.put("user_name", "admin");
    25         claims.put("nick_name","DASDA121");
    26         //创建JWT
    27         JwtBuilder builder = Jwts.builder().setClaims(claims)
    28                 //设置jwt的id
    29                 .setId(jwtId)
    30                 //设置签证时间
    31                 .setIssuedAt(now)
    32                 //设置这个jwt的持有人,譬如加上持有人的id
    33                 .setSubject(subject)
    34                 //设置签名算法以及密匙
    35                 .signWith(signatureAlgorithm, TOKEN_SECRET);
    36         //设置过期时间
    37         if(liveMillis >= 0){
    38             long endMillis = nowMillis + liveMillis;
    39             Date end = new Date(endMillis);
    40             builder.setExpiration(end);
    41         }
    42         return builder.compact();
    43     }
    44 
    45     /**
    46      * 解析jwt,变成你能验证的样子
    47      * @param jwt
    48      * @return
    49      */
    50     public static Claims parsJwt(String jwt){
    51         Claims claims = null;
    52         try {
    53             claims = Jwts.parser()
    54                     .setSigningKey(TOKEN_SECRET)
    55                     .parseClaimsJws(jwt).getBody();
    56         } catch (ExpiredJwtException e) {
    57             System.out.println("Token已过期");
    58         } catch (UnsupportedJwtException e) {
    59             System.out.println("Token格式错误");
    60         } catch (MalformedJwtException e) {
    61             System.out.println("Token没有被正确构造");
    62         } catch (SignatureException e) {
    63             System.out.println("签名失败");
    64         } catch (IllegalArgumentException e) {
    65             System.out.println("非法参数异常");
    66         }
    67         return claims;
    68     }
    69 
    70     /**
    71      *自定义想要验证的东西,你存在subject里面的东西。
    72      * @param token
    73      * @return
    74      */
    75     public static Boolean isToken(String subject, String token){
    76         //获取解析后的属性体
    77         try {
    78             Claims claims  = null;
    79             claims = parsJwt(token);
    80             //下面判断这个token除了格式之外你想判断的东西。测试自定义的subject为"{id:100,name:xiaohong}"
    81             if( subject.equals(claims.getSubject())){
    82                 return true;
    83             }else{
    84                 return false;
    85             }
    86         } catch (Exception e) {
    87             return false;
    88         }
    89 
    90     }
    91 
    92 }

     拦截器

     1 @Component
     2 public class JwtFilter implements HandlerInterceptor {
     3     private static String[] IGNORE_URL = {"/user/login"};
     4     private static Logger log = LoggerFactory.getLogger(JwtFilter.class);
     5     @Override
     6     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
     7         System.out.println("-----------------开始进行地址拦截------------------------");
     8         String token = request.getHeader("token");
     9         String username = String.valueOf(GetRequestBody.get(request, "username"));
    10         if(!"".equals(token) && token != null){
    11             if(!JWTUtil.isToken(username, token)){
    13                 return false;
    14             }
    15         }
    16 
    17         return true;
    18     }
    19     @Override
    20     public  void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    21 
    22     }
    23     @Override
    24     public  void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    25 
    26     }
    27 }

    controller使用情况

     1 @RequestMapping("/user")
     2 public class UserController {
     3     private static final long TOKEN_LIVE = 15 * 60 * 1000;
     4     @Autowired
     5     private UserService userService;
     6     @Autowired
     7     private RedisUtil redisUtil;
     8 
     9     /**
    10      * 返回两个参数 0 ,1 1表示验证通过,0 表示验证失败
    11      * @param username
    12      * @param password
    13      * @param request
    14      * @param response
    15      * @return
    16      */
    17     @RequestMapping("/login")
    18     public R login(String username , String password, HttpServletRequest request, HttpServletResponse response) throws IOException {
    19         User user = null;
    20         List<User> list = userService.sel(username, null);
    21         if(list.size() == 0 || list.isEmpty()){
    22             return R.ok("0");
    23         }
    24         System.out.println(request.getHeader("token"));
    25         if(redisUtil.get(username) == null){
    26             user = list.get(0);
    27             System.out.println("数据库查询");
    28             if(user != null && user.getPassword().equals(password)){
    29                 redisUtil.set(username, user);
    30                 redisUtil.expire(username, 15 * 60 * 1000);
    31                 response.setHeader("token", JWTUtil.setToken(username, 5 * 1000));
    32                 System.out.println(JWTUtil.setToken(username, 5 * 1000));
    33                 return R.ok("1");
    34             }else{
    35                 return R.ok("0");
    36             }
    37         }else{
    38             user = (User) redisUtil.get(username);
    39             System.out.println("缓存查询");
    40             if(user != null && user.getPassword().equals(password)){
    41                 return R.ok("1");
    42             }else{
    43                 return R.ok("0");
    44             }
    45         }
    46     }
    47 }

    结合了缓存 来做,在这里也把缓存工具类放上

      1 import org.springframework.beans.factory.annotation.Autowired;
      2 import org.springframework.data.redis.core.RedisTemplate;
      3 import org.springframework.stereotype.Component;
      4 
      5 
      6 import java.util.List;
      7 import java.util.Map;
      8 import java.util.Set;
      9 import java.util.concurrent.TimeUnit;
     10 
     11 @Component
     12 public class RedisUtil {
     13     @Autowired
     14     private RedisTemplate redisTemplate;
     15     //===================================common==================================
     16 
     17     /**
     18      * 指定缓存过期时间
     19      * @param key 主键
     20      * @param time 过期时间 秒
     21      * @return
     22      */
     23     public boolean expire(String key, long time){
     24         try{
     25             if(time > 0){
     26                 redisTemplate.expire(key, time, TimeUnit.SECONDS);
     27             }
     28             return true;
     29         }catch(Exception e){
     30             e.printStackTrace();
     31             return false;
     32         }
     33     }
     34 
     35     /**
     36      * 获取过期时间
     37      * @param key 主键
     38      * @return long时间
     39      */
     40     public long getExpire(String key){
     41         return redisTemplate.getExpire(key);
     42     }
     43 
     44     /**
     45      * 判断key是否存在
     46      * @param key
     47      * @return
     48      */
     49     public boolean hasKey(String key){
     50         try{
     51             return redisTemplate.hasKey(key);
     52         }catch (Exception e){
     53             return false;
     54         }
     55     }
     56 //============================String======================================
     57 
     58     /**
     59      * 存入缓存
     60      * @param key
     61      * @param value
     62      * @return
     63      */
     64     public boolean set(String key, Object value){
     65         try{
     66             redisTemplate.opsForValue().set(key, value);
     67             return true;
     68         }catch(Exception e){
     69             e.printStackTrace();
     70             return false;
     71         }
     72     }
     73     /**
     74      * 删除缓存
     75      * 未完成!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
     76      * @param key 主键可以单个或者集合
     77      */
     78     public void del(String... key){
     79         if(key.length > 0 && key != null){
     80             if(key.length == 1 ){
     81                 redisTemplate.delete(key);
     82             }else{
     83                 redisTemplate.delete(key);
     84             }
     85         }
     86     }
     87 
     88     /**
     89      * 获取缓存
     90      * @param key
     91      * @return
     92      */
     93     public Object get(String key){
     94         return key == null ? null : redisTemplate.opsForValue().get(key);
     95     }
     96 
     97     /**
     98      * 自增
     99      * @param key 主键
    100      * @param delta 增加多少(>0)
    101      * @return
    102      */
    103     public long incr(String key, long delta){
    104         if(delta < 0){
    105             throw new RuntimeException("递增因子必须大于零!");
    106         }else{
    107             return redisTemplate.opsForValue().increment(key, delta);
    108         }
    109     }
    110 
    111     /**
    112      * 递减
    113      * @param key 主键
    114      * @param delta 递减多少(>0)
    115      * @return
    116      */
    117     public long decr(String key, long delta){
    118         if(delta < 0){
    119             throw new RuntimeException("递减因子必须大于零!");
    120         }else{
    121             return redisTemplate.opsForValue().decrement(key, -delta);
    122         }
    123     }
    124 //==============================hash============================================
    125 
    126     /**
    127      * HashGet
    128      * @param key 键 不为 null
    129      * @param item 项 不为 null
    130      * @return
    131      */
    132     public Object hGet(String key, String item){
    133         return redisTemplate.opsForHash().get(key, item);
    134     }
    135 
    136     /**
    137      * 获取hashkey对应的所有key
    138      * @param key
    139      * @return
    140      */
    141     public Map<Object, Object> hmGet(String key){
    142         return redisTemplate.opsForHash().entries(key);
    143     }
    144 
    145     /**
    146      * hashset
    147      * @param key 键
    148      * @param map 对应的多个键值
    149      * @return
    150      */
    151     public boolean hSet(String key, Map<String, Object> map){
    152         try{
    153             redisTemplate.opsForHash().putAll(key, map);
    154             return true;
    155         }catch(Exception e){
    156             e.printStackTrace();
    157             return false;
    158         }
    159     }
    160 
    161     /**
    162      * hashset,并设置过期时间
    163      * @param key 键
    164      * @param map 对应的多个键
    165      * @param time 过期时间
    166      * @return
    167      */
    168     public boolean hmSet(String key, Map<String, Object> map, long time){
    169         try{
    170             redisTemplate.opsForHash().putAll(key, map);
    171             if(time > 0){
    172                 expire(key, time);
    173             }
    174             return true;
    175         }catch(Exception e){
    176             e.printStackTrace();
    177             return false;
    178         }
    179     }
    180 
    181     /**
    182      * 向缓存hash表中插入数据,如果表不存在则创建这个表
    183      * @param key 键
    184      * @param item map 键
    185      * @param value map值
    186      * @return
    187      */
    188     public boolean hSet(String key, String item, Object value){
    189         try{
    190             redisTemplate.opsForHash().put(key, item, value);
    191             return true;
    192         }catch(Exception e){
    193             e.printStackTrace();
    194             return false;
    195         }
    196     }
    197 
    198     /**
    199      * 向缓存中的map存入数据,如果map不存在则创建这个表,同时设置过期时间
    200      * @param key 键
    201      * @param item map键
    202      * @param value value
    203      * @param time 过期时间,如果这个map已经有过期时间,则会重置这个过期时间
    204      * @return
    205      */
    206     public boolean hSet(String key, String item, Object value, long time){
    207         try{
    208             redisTemplate.opsForHash().put(key, item, value);
    209             if(time > 0){
    210                 expire(key, time);
    211             }
    212             return true;
    213         }catch(Exception e){
    214             e.printStackTrace();
    215             return false;
    216         }
    217     }
    218 
    219     /**
    220      * hash删除数据
    221      * @param key 键 不为null
    222      * @param values 值 不为null
    223      */
    224     public void hDel(String key, Object... values){
    225         redisTemplate.opsForHash().delete(key, values);
    226     }
    227 
    228     /**
    229      *判断是否含有某个键
    230      * @param key 不为null
    231      * @param item 不为null
    232      * @return
    233      */
    234     public boolean hHasKey(String key, String item){
    235         return redisTemplate.opsForHash().hasKey(key, item);
    236     }
    237 
    238     /**
    239      * hash递增,如果不存在则创建,并把新增的值返回
    240      * @param key 键
    241      * @param item map键
    242      * @param by 增加多少(>0)
    243      * @return
    244      */
    245     public double hIncr(String key, String item, double by){
    246         return redisTemplate.opsForHash().increment(key, item, by);
    247     }
    248 
    249     /**
    250      * hash递减不存在就创建,并把新增的值返回
    251      * @param key 键
    252      * @param item map键
    253      * @param by 递减因子(>0)
    254      * @return
    255      */
    256     public double hDecr(String key, String item, double by){
    257         return redisTemplate.opsForHash().increment(key, item, -by);
    258     }
    259 //================================set=====================================
    260 
    261     /**
    262      * 获取key的所有set
    263      * @param key
    264      * @return
    265      */
    266     public Set<Object> sGet(String key){
    267         try{
    268             return redisTemplate.opsForSet().members(key);
    269         }catch(Exception e){
    270             e.printStackTrace();
    271             return null;
    272         }
    273     }
    274 
    275     /**
    276      * 判断key值是否含有value
    277      * @param key
    278      * @param value
    279      * @return
    280      */
    281     public boolean sHasKey(String key, Object value){
    282         try{
    283             return redisTemplate.opsForSet().isMember(key, value);
    284         }catch(Exception e){
    285             e.printStackTrace();
    286             return false;
    287         }
    288     }
    289 
    290     /**
    291      *将数据存入set缓存中
    292      * @param key 键
    293      * @param values 值
    294      * @return 成功个数
    295      */
    296     public long sSet(String key, Object... values){
    297         try{
    298             return redisTemplate.opsForSet().add(key, values);
    299         }catch(Exception e){
    300             e.printStackTrace();
    301             return 0;
    302         }
    303     }
    304 
    305     /**
    306      * 向缓存中添加数据,并设置过期时间,如果已经设置过期时间则重置
    307      * @param key 键
    308      * @param time 过期时间
    309      * @param values 值
    310      * @return 成功个数
    311      */
    312     public long sSetAndTime(String key, long time, Object... values){
    313         try{
    314             long count = redisTemplate.opsForSet().add(key,values);
    315             if(time > 0){
    316                 expire(key, time);
    317             }
    318             return count;
    319         }catch(Exception e){
    320             e.printStackTrace();
    321             return 0;
    322         }
    323     }
    324 
    325     /**
    326      * 获取set的长度
    327      * @param key
    328      * @return
    329      */
    330     public long sGetSetSize(String key){
    331         try{
    332             return redisTemplate.opsForSet().size(key);
    333         }catch(Exception e){
    334             e.printStackTrace();
    335             return 0;
    336         }
    337     }
    338 
    339     /**
    340      * 移除值为value的
    341      * @param key 键
    342      * @param values 值 可以是多个
    343      * @return 移除的个数
    344      */
    345     public long sRemove(String key, Object... values){
    346         try{
    347             long count = redisTemplate.opsForSet().remove(key, values);
    348             return count;
    349         }catch(Exception e){
    350             e.printStackTrace();
    351             return 0;
    352         }
    353     }
    354     //=====================list===============================
    355 
    356     /**
    357      * 获取list缓存
    358      * @param key 键
    359      * @param start 开始
    360      * @param end 结束 0 到 -1 所有值
    361      * @return
    362      */
    363     public List<Object> lGet(String key, long start, long end){
    364         try{
    365             return redisTemplate.opsForList().range(key, start, end);
    366         }catch(Exception e){
    367             e.printStackTrace();
    368             return null;
    369         }
    370     }
    371 
    372     /**
    373      * 获取list的长度
    374      * @param key 键
    375      * @return list长度
    376      */
    377     public long lGetListSize(String key){
    378         try{
    379             return redisTemplate.opsForList().size(key);
    380         }catch(Exception e){
    381             e.printStackTrace();
    382             return 0;
    383         }
    384     }
    385 
    386     /**
    387      * 通过index索引来获取value
    388      * @param key 键
    389      * @param index index 0 是第一个元素,-1是最后一个元素,-2是倒数第二个元素
    390      * @return
    391      */
    392     public Object lGetIndex(String key, long index){
    393         try{
    394             return redisTemplate.opsForList().index(key, index);
    395         }catch(Exception e){
    396             e.printStackTrace();
    397             return null;
    398         }
    399     }
    400 
    401     /**
    402      * 向list中插入数据
    403      * @param key 键
    404      * @param value 值
    405      * @return
    406      */
    407     public boolean lSet(String key, Object value){
    408         try{
    409            redisTemplate.opsForList().rightPush(key, value);
    410            return true;
    411         }catch(Exception e){
    412             e.printStackTrace();
    413             return false;
    414         }
    415     }
    416 
    417     /**
    418      * 向list中插入value
    419      * @param key 键
    420      * @param value 值
    421      * @param time 过期时间
    422      * @return
    423      */
    424     public boolean lSetAndTime(String key, Object value, long time){
    425         try{
    426             redisTemplate.opsForList().rightPush(key, value);
    427             if(time > 0){
    428                 expire(key, time);
    429             }
    430             return true;
    431         }catch(Exception e){
    432             e.printStackTrace();
    433             return false;
    434         }
    435     }
    436 
    437     /**
    438      * 重写方法,设置list
    439      * @param key
    440      * @param list
    441      * @return
    442      */
    443     public boolean lSet(String key, List<Object> list){
    444         try{
    445            redisTemplate.opsForList().rightPush(key, list);
    446            return true;
    447         }catch(Exception e){
    448             e.printStackTrace();
    449             return false;
    450         }
    451     }
    452 
    453     /**
    454      * 插入list并设置过期时间
    455      * @param key 键
    456      * @param list value
    457      * @param time 过期时间
    458      * @return
    459      */
    460     public boolean lSetAndTime(String key, List<Object> list, long time){
    461         try{
    462             redisTemplate.opsForList().rightPush(key,list);
    463             if(time > 0){
    464                 expire(key, time);
    465             }
    466             return true;
    467         }catch(Exception e){
    468             e.printStackTrace();
    469             return false;
    470         }
    471     }
    472 
    473     /**
    474      * 更新数据
    475      * @param key
    476      * @param index
    477      * @param value
    478      * @return
    479      */
    480     public boolean lUpdateIndex(String key, long index, Object value){
    481         try{
    482             redisTemplate.opsForList().set(key, index, value);
    483             return true;
    484         }catch(Exception e){
    485             e.printStackTrace();
    486             return false;
    487         }
    488     }
    489 
    490     /**
    491      * 移除count个值是value的
    492      * @param key 键
    493      * @param count 移除数量
    494      * @param value 值
    495      * @return
    496      */
    497     public long lRemove(String key, long count, Object value){
    498         try{
    499             long num = redisTemplate.opsForList().remove(key, count, value);
    500             return num;
    501         }catch(Exception e){
    502             e.printStackTrace();
    503             return 0;
    504         }
    505     }
    506 }
    View Code

     遇到的问题

    1.上手是逻辑混乱,不知道token的颁发和判断在哪里发生

    后来确定 controller负责token的颁发,拦截器来判断token是否合法和过期情况。

    2.我把user.username属性作为判断token构造的一个属性,但是我不知道怎么在request的body中获取这个username属性

    对此我直接封装了一个方法,把request和你想要的属性当作参数传入,然后获取参数的属性。

     1 import javax.servlet.http.HttpServletRequest;
     2 import java.util.Enumeration;
     3 import java.util.HashMap;
     4 import java.util.Map;
     5 
     6 public class GetRequestBody {
     7     public static Object get(HttpServletRequest request, String string) {
     8         Map<String,Object> map = new HashMap<String,Object>();
     9         Enumeration paramNames = request.getParameterNames();
    10         while (paramNames.hasMoreElements()) {
    11             String paramName = (String) paramNames.nextElement();
    12 
    13             String[] paramValues = request.getParameterValues(paramName);
    14             if (paramValues.length >0) {
    15                 String paramValue = paramValues[0];
    16                 if (paramValue.length() != 0) {
    17                     map.put(paramName, paramValue);
    18                 }
    19             }
    20         }
    21         return map.get(string);
    22 
    23     }
    24 
    25 }

     3.我认为user第一次验证的时候token都为空,所以当token为空的时候我选择了直接放行,但是总觉得不是这么回事,所以我觉得这是一个很大的问题。我在想要不要定义一个统一的默认token当user第一次来的时候带着这个token来进行判断。

    但是前端token的拼装我并不了解,也不擅长所以就耽搁了。

     
     
     
     
     
     
     
     
  • 相关阅读:
    2019-9-2-Visual-Studio-自定义项目模板
    2018-8-10-WPF-判断调用方法堆栈
    2018-8-10-WPF-判断调用方法堆栈
    2018-8-10-VisualStudio-自定义外部命令
    2018-8-10-VisualStudio-自定义外部命令
    Java实现 LeetCode 999 车的可用捕获量(简单搜索)
    向代码致敬,寻找你的第83行(阿里巴巴的第83行代码是什么梗)
    向代码致敬,寻找你的第83行(阿里巴巴的第83行代码是什么梗)
    向代码致敬,寻找你的第83行(阿里巴巴的第83行代码是什么梗)
    Java实现 LeetCode 557 反转字符串中的单词 III(StringBuilder的翻转和分割)
  • 原文地址:https://www.cnblogs.com/frank9571/p/12630911.html
Copyright © 2011-2022 走看看