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 }
相關文章
相關標籤/搜索