项目背景: 工程需要一个传输数据的接口,为了安全问题,我们加入一个获取令牌的接口,令牌是一个随机码,可设置失效时间,只有拿到令牌之后才可以带着令牌访问数据接口,我们通过比对令牌来确保调用者的合法性。
这是一个很简单的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有着很多高级的用法和功能后面我们再深入探讨一起学习把。