token是"令牌","代幣"等一些說法,關於token的作用的說法也比較多,其實在token的使用中保證業務的安全性上具有不可忽略的作用,比如防止表單的重復提交,token的生成可以用session的隨機id,也可以用隨機數等,前者可以直接將session的ID以token的形式傳入,並且封裝為需要的cookie放進response中返回給服務端,下面是一個關於隨機數生成token的簡單的業務應用場景:用戶忘記密碼時驗證問題答案重置自己的密碼的過程。
(1)TokenCache類
將生成的token的值加載到本地緩存中:
1 /** 2 * @author 大神愛吃茶 3 * */ 4 public class TokenCache { 5 6 //創建日志 7 private static Logger logger = LoggerFactory.getLogger(TokenCache.class); 8 9 //token的前綴 10 public static final String TOKEN_PREFIX = "token_"; 11 12 //設置本地緩存,其中expireAfterAccess是有效期 13 //初始化的值是10000,如果超過10000的話就會使用LRU算法 14 //聲明了localCache的key和value值都是String類型的 15 private static LoadingCache<String,String> localCache = CacheBuilder.newBuilder(). 16 initialCapacity(1000). 17 maximumSize(10000). 18 expireAfterAccess(12, TimeUnit.HOURS). 19 build(new CacheLoader<String, String>() { 20 //下面這個方法是默認的數據加載實現,當調用get取值的時候,如果key沒有對應的值,就調用這個方法來進行加載 21 //即如果get沒有拿到數據,就用load來加載緩存 22 @Override 23 public String load(String s) throws Exception { 24 //在這里可以初始化加載數據的緩存信息,讀取數據庫中信息或者是加載文件中的某些數據信息 25 return "null"; 26 } 27 }); 28 29 public static void setKey(String key,String value){ 30 localCache.put(key, value); 31 } 32 33 public static String getKey(String key){ 34 String value = null; 35 try{ 36 value = localCache.get(key); 37 if("null".equals(value)){ 38 return null; 39 } 40 return value; 41 }catch (Exception e){ 42 logger.error("localCache get error", e); 43 } 44 return null; 45 } 46 }
(2)業務接口和實現類即方法
①忘記密碼接口:
1 /** 2 * 忘記密碼接口,返回這個用戶的問題給他 3 * */ 4 @RequestMapping(value = "forget_get_question.do",method = RequestMethod.POST) 5 @ResponseBody 6 public ServerResponse<String> forgetPasswordGetQuestion(String username){ 7 ServerResponse forgetPwdGetQuestionResponse = iUserService.selectUserQuestion(username); 8 return forgetPwdGetQuestionResponse; 9 }
②校驗問題的答案是否正確接口
1 /** 2 * 校驗問題的答案是否准確接口 3 * */ 4 @RequestMapping(value = "forget_check_answer.do",method = RequestMethod.POST) 5 @ResponseBody 6 public ServerResponse<String> forgetPasswordCheckAnswer(String username,String question,String answer){ 7 return iUserService.checkAnswer(username,question,answer); 8 }
checkAnswer方法:並且將生成的forgetPwdToken返回給前端供后面驗證!
1 public ServerResponse<String> checkAnswer(String username,String question,String answer){ 2 int resultCount = userMapper.checkAnswer(username,question,answer); 3 if(resultCount > 0){ 4 //生成一個UUID序列,然后再考慮將之加到緩存中去 5 String forgetPwdToken = UUID.randomUUID().toString(); 6 //setKey的過程localCache.put(key, value)會把key和對應的token的值加進去 7 TokenCache.setKey(TokenCache.TOKEN_PREFIX+username, forgetPwdToken); 8 return ServerResponse.createBySuccess(forgetPwdToken); 9 } 10 return ServerResponse.createByError("問題的答案錯誤"); 11}
③忘記密碼重置密碼接口
1 /** 2 * 忘記密碼,重置密碼接口 3 * */ 4 @RequestMapping(value = "forget_reset_password.do",method = RequestMethod.POST) 5 @ResponseBody 6 public ServerResponse<String> forgetPasswordRestPassword(String username,String passwordNew,String forgetToken){ 7 return iUserService.forgetPwdRestPwd(username,passwordNew,forgetToken); 8 }
forgetPwdRestPwd方法:
1 /** 2 * 忘記密碼,重置密碼 3 * */ 4 //是真要傳入通過checkAnswer驗證了答案之后返回的token的 5 public ServerResponse<String> forgetPwdRestPwd(String username,String passwordNew,String forgetPwdToken){ 6 //先進行校驗 7 if(StringUtils.isBlank(forgetPwdToken)){ 8 return ServerResponse.createByError("參數錯誤,token需要傳遞"); 9 } 10 //如果用戶名為空的話用戶可能就會直接獲取token_和forgetToken的內容,對username再進行校驗 11 ServerResponse ifUsernameIsNullResponse = this.checkValid(username, Const.USERNAME); 12 if(ifUsernameIsNullResponse.isSuccess()){ 13 //因為checkValid的時候,校驗成功是用戶不存在的情況 14 return ServerResponse.createByError("用戶不存在"); 15 } 16 //在本地緩存中取TokenCache.TOKEN_PREFIX + username對應的token的值 17 String token = TokenCache.getKey(TokenCache.TOKEN_PREFIX + username); 18 //StringUtils的equals(a,b)方法即使a為空也不會報異常,但如果是普通的object的equals方法就會報空指針異常 19 if(StringUtils.isBlank(token)){ 20 return ServerResponse.createByError("token無效或者已過期"); 21 } 22 //需要在此處傳進forgetPwdToken來與緩存中的token來比較驗證是否是在同一時間段的該用戶在修改自己的密碼保證安全性 23 if(StringUtils.equals(forgetPwdToken, token)){ 24 String md5Password = MD5Util.MD5EncodeUtf8(passwordNew); 25 //更新用戶的密碼 26 int resultCount = userMapper.updatePasswordByUsername(username,md5Password); 27 if(resultCount > 0 ){ 28 return ServerResponse.createBySuccess("修改密碼成功"); 29 } 30 }else { 31 return ServerResponse.createByError("token錯誤,請重新獲取重置密碼的token"); 32 } 33 return ServerResponse.createByError("修改密碼失敗"); 34 }
在上面,驗證完用戶密碼的正確與否就是最后的修改重置密碼的最后一步了,所以需要驗證是否是相同的用戶在修改自己的密碼,這樣能防止惡意的篡改,使用生成的token能保證安全性,先生成后存到緩存中,並傳回前端,這樣保證在后面需要的時候再傳入此token與緩存中的token進行比較驗證是否正確。在規定的時效內修改密碼保證是相同的用戶。