JWT实战

JWT实战

JWT认证流程java

先来回顾下JWT的流程,jwt是存储在客户端的,服务器不须要存储jwt;客户端每次发送请求时携带token,而后到服务端验证token是否正确,是否过时,而后解码出携带的用户信息。redis

存在的问题 
一、Token失效问题: 
好比在浏览器端经过用户名/密码验证得到签名的Token被木马窃取。即便用户登出了系统,黑客仍是能够利用窃取的Token模拟正常请求可用它访问服务器,而服务器端对此彻底不知道,(由于JWT机制是无状态的),直到过时,中间服务器没法控制它.算法

二、 app类Token的有效时间浏览器

token的有效时间:

        1. 若是 app 是新闻类/游戏类/聊天类等须要长时间用户粘性的. 通常能够设置1年的有效时间!

        2. 若是 app 是 支付类/银行类的. 通常token只得有效时间比较短: 15分钟左右!
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

咱们的解决方法是: 
服务端使用Redis缓存服务器签发给已登陆用户的Token, 
每次客户端发送请求时到redis中查该用户 请求的token 和 redis存的token是否一致,不一致不容许token登陆, 
若是一致,判断这个token是否能够用(主要防止修改密码和注销操做的token没失效问题) 
最后返回用户信息 
当用户修改密码和注销时直接将redis中该用户的Token设置失效。下次经过token登陆,会提醒token失效,要从新登陆,咱们从新生成一个新的token给用户。经过redis存储token,实现主动控制 token过时失效的问题了。缓存

封装的JWT工具类 部分代码 
生成Token码服务器

/** * 生成jwt token * @param userId * @param expireTime 过时时间戳 * @return */ public static String makeToken(String userId,Date expireTime){ long nowMillis = System.currentTimeMillis(); Date now=new Date(nowMillis);//签发时间精度:毫秒 try { Algorithm algorithm = Algorithm.HMAC256(MyConstance.JWT_SECRET); return JWT.create().withIssuer(userId).withIssuedAt(now).withExpiresAt(expireTime).sign(algorithm); } catch (UnsupportedEncodingException exception){ exception.printStackTrace(); } catch (JWTCreationException exception){ exception.printStackTrace(); } return null; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

解码和验证Token码markdown

/** * 校验toekn是否有效 * @param userId * @param token * @return */ public static boolean verifyToken(String userId,String token){ boolean active = true; try { Algorithm algorithm = Algorithm.HMAC256(MyConstance.JWT_SECRET);//声明签名所用的算法和秘钥 JWTVerifier verifier = JWT.require(algorithm).withIssuer(userId).build(); verifier.verify(token); } catch (TokenExpiredException exception){ //System.out.println("--- token 过时"); active = false; } catch (JWTDecodeException exception){ //System.out.println("--- token 无效"); active = false; } catch (UnsupportedEncodingException exception){ //System.out.println("--- token 无效"); active = false; } catch (JWTVerificationException exception){ //System.out.println("--- token 错误"); active = false; } return active; } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

登陆app

@ApiOperation(value = "登陆") @RequestMapping(value = "/loginWEB", method = RequestMethod.POST) public ResponseEntity<BaseResult> loginWEB(HttpServletRequest request, HttpServletResponse response, @ApiParam("用户名") @RequestParam(value = "username", required = false) String username, @ApiParam("密码") @RequestParam(value = "password", required = false) String password) { XkbbUser user=userService.getBy("username", username); //帐号密码校验业务 if(user==null) { return buildFailedInfo(ApiConstance.USER_NOT_EXIST); } if(!password.equals(user.getPassword())) { return buildFailedInfo(ApiConstance.PASSWORD_ERROR); } //获取用户id String userId=user.getId(); //设置token过时时间为30天以后 Date expireTime = Tool.datePlu(new Date(), 30); //生成JWT(JSon Web Token) String token=TokenUtil.makeToken(userId, expireTime); //更新缓存里面该用户的token: // 若是已登陆,则使其Token失效 TokenUtil.updateTokenAPP(userId, token); Map<String, Object> map = new HashMap<String, Object>(); map.put("token", token);//服务器生成的token return buildSuccessInfo(map); } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

MyConstance类部分全局常量定义 
/** 
* token的缓存map名 
*/ 
public static final String KEY_TOKEN_MAP = “key_token_map”; 
/** 
* JWT生成token时加密用的secret 
*/ 
public static final String JWT_SECRET = “3MZq0BYyGcXYoXjhS4QbAM+2YdlLCwKRr2gvVJOJ”;工具

/** * 用户登陆校验 * @param userId * @param token * @return */ //到redis中查该用户 请求的token 和 redis存的token是否一致,不一致不容许token登陆, //再次根据建立时间,判断这个token是否能够用(主要防止出现修改密码和注销操做的token没失效问题) //失效则Redis中删除该token public static boolean isLogin(String userId,String token){ if(Tool.isNotBlank(userId)) { if(Tool.isNotBlank(token)) { //到redis中查该用户 请求的token 和 redis存的token是否一致,不一致不容许token登陆 String existValue = (String) CacheUtil.hget(MyConstance.KEY_TOKEN_MAP, userId); //System.out.println("existValue:"+existValue); if(Tool.isNotBlank(existValue) && existValue.equals(token)) { //再次根据建立时间,判断这个token是否可有效(主要防止出现修改密码和注销操做的token没失效问题) boolean isLogin = verifyToken(userId,token); //失效则Redis中删除该token if(!isLogin){ CacheUtil.hdel(MyConstance.KEY_TOKEN_MAP,userId); } return isLogin; } } } return false; } /** * 清除用户缓存token * @param userId * @return */ public static String clearToken(String userId){ CacheUtil.hdel(MyConstance.KEY_TOKEN_MAP, userId); } /** * 更新用户token * @param userId * @param value */ public static void updateToken(String userId,String value){ clearToken(userId);//清除缓存中旧的Token CacheUtil.hset(MyConstance.KEY_TOKEN_MAP, userId, value);//缓存新的Token }
相关文章
相关标签/搜索