Alt_Shift

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

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的拼装我并不了解,也不擅长所以就耽搁了。

 

 

 
 
 
 
 
 
 
 
posted on 2020-04-04 11:15  Alt_Shift  阅读(1749)  评论(0编辑  收藏  举报