Guava Cache和token

最近跟着視頻學習,學習用到了Guava Cache和token機制,就去百度了下找到一個原文:https://blog.csdn.net/qq_28021299/article/details/80541755 。加了一點點本身的理解。java

    功能需求是這樣的,在客戶端用戶登陸時忘記密碼,須要找回密碼,經過正確輸入找回密碼答案來訪問服務端的修改密碼接口。爲了防止惡意用戶來直接訪問修改密碼接口,假如沒有taken那麼就能夠修改任意用戶密碼。(經典的橫向越權)算法

,在調用驗證答案接口後採用token機制來驗證身份,並用Guava Cache作一個定時的token來保證安全性。數據庫

理解token機制
    什麼是token?
    token的意思是「令牌」,是服務端生成的一串字符串,做爲客戶端進行請求的一個標識。當用戶第一次登陸後,服務器生成一個token並將此token返回給客戶端,之後客戶端只需帶上這個token前來請求數據便可,無需再次帶上用戶名和密碼。緩存

    身份認證概述:
    因爲HTTP是一種沒有狀態的協議,它並不知道是誰訪問了咱們的應用。這裏把用戶當作是客戶端,客戶端使用用戶名還有密碼經過了身份驗證,不過下次這個客戶端再發送請求時候,還得再驗證一下。
通用的解決方法就是,當用戶請求登陸的時候,若是沒有問題,在服務端生成一條記錄,在這個記錄裏能夠說明登陸的用戶是誰,而後把這條記錄的id發送給客戶端,客戶端收到之後把這個id存儲在cookie裏,下次該用戶再次向服務端發送請求的時候,能夠帶上這個cookie,這樣服務端會驗證一下cookie裏的信息,看能不能在服務端這裏找到對應的記錄,若是能夠,說明用戶已經經過了身份驗證,就把用戶請求的數據返回給客戶端。
以上所描述的過程就是利用session,那個id值就是sessionid。咱們須要在服務端存儲爲用戶生成的session,這些session會存儲在內存,磁盤,或者數據庫。
    基於token機制的身份認證:
    使用token機制的身份驗證方法,在服務器端不須要存儲用戶的登陸記錄。大概的流程:
客戶端使用用戶名和密碼請求登陸。服務端收到請求,驗證用戶名和密碼。驗證成功後,服務端會生成一個token,而後把這個token發送給客戶端。客戶端收到token後把它存儲起來,能夠放在cookie或者Local Storage(本地存儲)裏。客戶端每次向服務端發送請求的時候都須要帶上服務端發給的token。服務端收到請求,而後去驗證客戶端請求裏面帶着token,若是驗證成功,就向客戶端返回請求的數據。安全

   Guava Cache:
Guava Cache是單個應用運行時的本地緩存。它不把數據存放到文件或者外部服務器上。簡單、強大、及輕量級。它不須要配置文件,使用起來和ConcurrentHashMap同樣簡單,並且能覆蓋絕大多數使用cache的場景需求服務器

 

   例子:
    用戶登陸校驗答案正確後,在service層來生成一個惟一token,通常可使用mac地址,或者sessionId來生成token。由於token是能夠被截獲的,很是容易泄露,若是不進行加密,很容易被惡意拷貝並用來登陸。因此通常會對token進行加密處理。
通常能夠在存儲的時候把token進行對稱加密存儲,用到的時候再解密,或者使用請求URL、時間戳、token三者合併,經過算法進行加密處理。兩個一塊用更安全。這裏就簡單利用UUID來實現生成惟一token
    
String forgetToken = UUID.randomUUID().toString();
而後再把token存到本地Guava Cache內存緩存中,在響應對象中也把這個token封裝起來響應給客戶端。到時候用戶改密,就能夠傳入該token實現token機制。
TokenCache.setKey("Token_"+username,forgetToken);
Guava Cache實現
package com.lsmall.common;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

public class TokenCache {
// 建立logback的logger
private static Logger logger = LoggerFactory.getLogger(TokenCache.class);
// 聲明一個靜態的內存塊,guava裏面的本地緩存
private static LoadingCache<String, String> localcache =
//構建本地緩存,調用鏈的方式 ,1000是設置緩存的初始化容量,maximumSize是設置緩存最大容量,當超過了最大容量,guava將使用LRU算法(最少使用算法),來移除緩存項
//expireAfterAccess(12,TimeUnit.HOURS)設置緩存有效期爲12個小時
CacheBuilder.newBuilder().initialCapacity(1000).maximumSize(10000).expireAfterAccess(12,TimeUnit.HOURS)
//build裏面要實現一個匿名抽象類
.build(new CacheLoader<String, String>() {
// 這個方法是默認的數據加載實現,get的時候,若是key沒有對應的值,就調用這個方法進行加載

@Override
public String load(String s) throws Exception {
// 爲何要把return的null值寫成字符串,由於到時候用null去.equal的時候,會報空指針異常
return "null";
}
});

/*
* 添加本地緩存
* */
public static void setKey(String key, String value) {
localcache.put(key, value);
}

/*
* 獲得本地緩存
* */
public static String getKey(String key) {
String value = null;
try {
value= localcache.get(key);
if ("null".equals(value)) {
return null;
}
return value;
} catch (ExecutionException e) {
logger.error("getKey()方法錯誤",e);
}
return null;
}
}cookie


Guava Cache的回收策略
有兩種回收策略:session

一種是基於容量的回收CacheBuilder.maximumSize(Long)。設置緩存最大容量,當超過最大容量,緩存將嘗試回收最近沒有使用或整體上不多使用的緩存項。dom

第二種定時回收ide

expireAfterAccess(long,TimeUnit):緩存項在給定時間內沒有被讀寫訪問,則回收。請注意這種緩存的回收順序和基於大小回收同樣。

  expireAfterWrite(long,TimeUnit):緩存項在給定時間內沒有被寫訪問(建立或覆蓋),則回收。若是認爲緩存數據老是在固定時候後變的陳舊不可用,這種回收是可取的。

實例中用的是第一種策略,經過設置緩存最大容量,當超過了最大容量,guava將使用LRU算法來減小緩存項

LRU算法原理
LRU(Least recently used,最近最少使用的)算法根據數據的歷史訪問記錄來進行淘汰數據,其核心思想是「若是數據最近被訪問過,那麼未來被訪問的概率也更高。

最多見的實現是使用一個鏈表保存緩存數據:1 新數據插入到鏈表頭部2 每當緩存命中(即緩存數據被訪問),則將數據移到鏈表頭部3 當鏈表滿的時候,將鏈表尾部的數據丟棄

相關文章
相關標籤/搜索