項目背景: 工程需要一個傳輸數據的接口,爲了安全問題,我們加入一個獲取令牌的接口,令牌是一個隨機碼,可設置失效時間,只有拿到令牌之後纔可以帶着令牌訪問數據接口,我們通過比對令牌來確保調用者的合法性。
這是一個很簡單的web需求,我們只需要把返回的令牌存在一個地方然後等待再次調用的時候拿出來對比一下就ok,於是我想到了session,因爲session可以幫你管理失效時間,其本身的調用又很方便,代碼寫好之後瀏覽器測試一下沒問題,是我們想要的結果。
但是這裏我忽略的一個比較大的問題,我們用網頁調用接口的時候,網頁是可以幫我們管理sessionid的,sessionid隨着每次請求到服務器找到對用的session然後找到對應的值,只有你網頁不關,sessionid不會變,但是在實際生產過程中,我們代碼卻無法管理sessionid,就是說我們獲取到token之後下次再調用別的接口的時候服務器無法像之前一樣根據sessionid匹配到session,這樣我們之前存在session中的值找不到了,也就沒法做token驗證了
解決思路:
1. 我們使用長鏈接,把調用接口一和接口二的代碼寫在一個連接裏面,這樣就保證了兩次調用面對同一個session,token驗證就可行了。但是這樣的缺點很明顯,就是我們每次調用接口的時候都必須兩個接口一起調用,耦合度太高,極不靈活
2. 把token存在application裏面,application面向全局,也很容易把token存在其中,但是需要我們手工管理時效時間,這樣就很繁瑣
終極解決方案: 緩存的應用
3.把token放在緩存裏面,這個方案倒是可行,提到緩存我首先想到redis,redis是緩存的典型應用,速度快,穩定性好,但是放在這個工程中就有點大材小用,畢竟我們是存儲kb級別的token,今天來介紹一下java原生緩存框架Ehcache
Ehcache在本次工程中的應用極其輕便,其原理也很簡單,話不多說,先看下Ehcache的存取流程
CacheManager manager = CacheManager.create();
Cache cache = manager.getCache("defaultCache");//對應xml的配置 後面會說
//element就是一個標籤,我們的每個鍵值對對應每個element
Element ele = new Element("name","hello world");
ele.setTimeToLive(12);
//存入緩存
cache.put(ele);
//獲取的時候更簡單,直接獲取.get(key)
Element element = cache.get("name");
//對應的值
Object obj = element.getObjectValue();
System.out.println((String) obj);
<!---------------上個demo看下效果--------------->
首先要有一個配置文件ehcache.xml (下載jar的時候,jar裏面會有一個default.xml 把它拷貝出來改名爲ehcache.xml放到工程文件夾裏面就行了)
<!-----------------------ehcache.xml --------------------------------->
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</defaultCache>
<cache name="demo"//demo 對應之前的那個defaultCache
maxElementsInMemory="10000"//大小限制,超出溢寫磁盤
eternal="false"//是否永久存儲
overflowToDisk="true"//溢出是否寫磁盤
timeToIdleSeconds="300"//沒有訪問的時候多少秒消亡
timeToLiveSeconds="600"//總的存活時間
memoryStoreEvictionPolicy="LFU" />//算法策略 自己百度
</ehcache>
<!-----------------------單例獲取cache --------------------------------->
public class Ehcache {
private static Cache cache ;
public static synchronized Cache getIntance(){
if(cache != null){
return cache;
}else{
CacheManager manager = CacheManager.create();
cache = manager.getCache("demo");
return cache;
}
}
}
<!-----------------------測試類 --------------------------------->
public class Test {
public static void main(String[] args) throws InterruptedException {//主方法存數據 另起線程取數據
CacheManager manager = CacheManager.create();
Cache cache = manager.getCache("demo");
Element ele = new Element("name","hello world");
ele.setTimeToLive(12);
cache.put(ele);
Element element = cache.get("name");
Object obj = element.getObjectValue();
System.out.println((String) obj);
Thread.sleep(3000);
ThreadAction action = new ThreadAction();
action.setCacheName("demo");
action.setManager(manager);
action.start();
System.out.println("----main thread is return !----");
}
}
class ThreadAction extends Thread{
private String cacheName;
private CacheManager manager;
@Override
public void run(){
while(true){
try {
Thread.sleep(3000);
Cache cache = manager.getCache(cacheName);
Element element = cache.get("name");
System.out.println("從緩存中取數據:"+element.getObjectValue().toString());
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassCastException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
運行結果:
----main thread is return !----
從緩存中取數據:hello world
從緩存中取數據:hello world
看下Ehcache結合spring的應用:
<!----------applicationContext.xml--------->
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:spring/applicationContext-ehcache.xml"></property>
</bean>
<!----------applicationContext-ehcache.xml--------->
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<!-- 指定一個文件目錄,當EhCache把數據寫到硬盤上時,將把數據寫到這個文件目錄下 -->
<diskStore path="java.io.tmpdir"/>
<!-- 設定緩存的默認數據過期策略 -->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="10"
timeToLiveSeconds="20"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"/>
<cache name="tokenCache"
maxElementsInMemory="1000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="10"
timeToLiveSeconds="20"/>
</ehcache>
<!----------controller注入cacheManager--------->
@Autowired
private CacheManager cacheManager;
//拿cache
Cache cache = cacheManager.getCache(ParsePropUtil.getProp(ConfigurtionManager.TOKEN_CACHE_NAME));
Element ele = cache.get(apiKey);
ele.setTimeToIdle(invalidTime); //設置最後一次訪問後的時效時間
ele.setTimeToLive(Integer.MAX_VALUE);//設置全過程的時效時間
cache.put(ele);
然後就可以隨心所欲的在緩存中存取數據設置失效時間了,問題解決,本文簡單介紹了Ehcache在本次工程中的應用,Ehcache有着很多高級的用法和功能後面我們再深入探討一起學習把。