封裝Map實現緩存並利用其判斷登陸是否過時

參考網頁

http://www.javashuo.com/article/p-xgswbwju-n.htmljava

說明

例子是以本身在作的實際項目爲例,單服務實例。web

只是演示用,實際項目中不推薦使用。spring

單服務實例能夠用Guava等。分佈式服務能夠用Redis等。數組

方案描述

  1. SpringBoot接收到http請求
  2. 攔截器攔截請求,如果登陸(或其餘特殊的請求),放過;若非登陸(或其餘特殊的請求),取出token並從token中取出userId(後發現token或cookie信息裏實際存儲的是userName,因此使用userName而非userId),根據userId(修改成userName)從緩存中取出上次登陸的時間loginTime,與如今的系統時間比對,若是在時間範圍內,則刷新最後登陸時間;若是超時,則攔截器返回,告知http請求登陸超時
  3. 【1】中如果登陸請求(或其餘特殊的請求)攔截器放過不進行攔截。登陸成功後將userId(修改成userName)和如今系統時間(loginTime)存入緩存中保存,留待後面訪問時進行超時驗證

代碼實現

本身建立的簡單的緩存類--SimpleCacheMgr

package com.richfit.ruiche.common.simpleCache;


import java.util.HashMap;

import java.util.Map;


/**

 * 簡單緩存管理類的實現:只是簡單封裝了 HashMap

 *

 * @author Administrator

 *

 */

public class SimpleCacheMgr {


private static Map cacheMap = new HashMap();


private static SimpleCacheMgr cm = null;


// 構造方法

private SimpleCacheMgr() {

}


public static SimpleCacheMgr getInstance() {

if (cm == null) {

cm = new SimpleCacheMgr();

}

return cm;

}


/**

 * 增長緩存

 *

 * @param key

 * @param value

 * @param ccm

 *            緩存對象

 * @return

 */

public boolean addCache(Object key, Object value) {

System.out.println("開始增長緩存-------------");

boolean flag = false;

try {

cacheMap.put(key, value);

System.out.println("增長緩存結束-------------");

System.out.println("now addcache==" + cacheMap.size());

flag = true;

} catch (Exception e) {

e.printStackTrace();

}


return flag;

}


/**

 * 獲取緩存實體

 */

public Object getValue(Object key) {

Object ob = cacheMap.get(key);

if (ob != null) {

return ob;

} else {

return null;

}

}


/**

 * 修改緩存實體

 */

public Object setValue(Object key, Object value){

return cacheMap.put(key, value);

}


/**

 * 獲取緩存數據的數量

 *

 * @return

 */

public int getSize() {

return cacheMap.size();

}


/**

 * 刪除緩存

 *

 * @param key

 * @return

 */

public boolean removeCache(Object key) {

boolean flag = false;

try {

cacheMap.remove(key);

flag = true;

} catch (Exception e) {

e.printStackTrace();

}

return flag;

}


}

攔截器 LoginTimeOutInterceptor

package com.richfit.ruiche.Interceptor;


import java.util.ArrayList;

import java.util.Date;

import java.util.List;


import javax.servlet.http.Cookie;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


import org.springframework.web.method.HandlerMethod;

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;


import com.richfit.ruiche.common.simpleCache.SimpleCacheMgr;

import com.richfit.ruiche.common.specialUrl.SpecialUrl;

import com.richfit.ruiche.util.LoginUtil;


/**

 *

 * Description:登陸超時驗證及刷新最新登陸時間

 *

 */

public class LoginTimeOutInterceptor extends HandlerInterceptorAdapter{



    @Override

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

    

     if (!(handler instanceof HandlerMethod)) {

            return true;

        }

        String servletPath = request.getServletPath();

        if (ifNeedCheck(servletPath)) {

            //驗證cookie

            try {

                Cookie[] cookies = request.getCookies();

                if (cookies != null) {

                    for (Cookie cookie : cookies) {

                        if (cookie.getName().equals("sec_login")) {

                            String userName = LoginUtil.decUserName(cookie.getValue(),0);

                            if (userName == null) {

                                return false;

                            } else {

                          /**

                           * 此處獲取登陸用戶名,從緩存中獲取並比對上次登陸時間,過時則沒法登陸;沒過時則更新最新登陸時間

                           */

//                          String loginName = DataAuthorityUtil.getLoginNameByCookies(request);

                             SimpleCacheMgr cm= SimpleCacheMgr.getInstance();

                             if(cm.getValue(userName) == null){

                             return false;

                             }else{

                             Long lastLoginTime = (Long) cm.getValue(userName);

                             //超過了必定的時間值--這裏固定寫成了10s

                             if(((new Date().getTime()) - lastLoginTime) > 10 * 1000){

                             System.out.println("======已通過期啦======");

                             return false;

                             }else{

                             //更新最近登陸時間

                             cm.setValue(userName, new Date().getTime());

                             System.out.println("======更新登陸時間了======");

                             return true;

                             }

                             }

                          /**

                           * 此處獲取登陸用戶名,從緩存中獲取並比對上次登陸時間,過時則沒法登陸;沒過時則更新最新登陸時間

                           */

                                

                            }

                        }

                        break;

                    }

                }

            } catch (Exception ex) {

                return false;

            }

        }

        return true;

    }

    

    /**

     *

     * Description:是否須要驗證鏈接判斷。能夠經過的URL:web端除【登陸】和【獲取新密碼】外全部的鏈接

     * @Version1.0 2017-2-16 上午11:38:36 by 丁兆寧 (dingzhaoning@cnpc.com.cn)

     * @param servletPath

     * @return

     */

    private boolean ifNeedCheck(String servletPath){

     List<String> noNeedCheckUrlList = getNoNeedCheckUrlList();

     for(String s:noNeedCheckUrlList){

     if(servletPath.matches(s)){

     return false;

     }

     }

     return true;

    }

    

    private List<String> getNoNeedCheckUrlList(){

     List<String> list = new ArrayList<String>();

     list.addAll(SpecialUrl.getNoNeedCheckUrlList());

     return list;

    }

}

配置類增長攔截器配置--只截取了部分代碼

/**

 *

 * 在這個類裏註冊自定義的攔截器

 */

@Configuration

@EnableWebMvc

@ComponentScan(basePackages = "com.richfit.ruiche.Interceptor")

public class WebAppConfig extends WebMvcConfigurerAdapter{


    @Bean

    public ReturnMessageInterceptor returnMessageInterceptor(){

        return new ReturnMessageInterceptor();

    }

   

    @Bean

    public ReturnMessageTimeHandlerInterceptor returnMessageTimeHandlerInterceptor(){

        return new ReturnMessageTimeHandlerInterceptor();

    }

    

    /**

     *

     * Description:web端登陸攔截器

     * @return

     */

    @Bean

    public LoginInterceptor loginInterceptor(){

        return new LoginInterceptor();

    }

    

    /**

     *

     * Description:web端訪問路徑權限攔截器

     * @return

     */

    @Bean

    public AccessInterceptor accessInterceptor(){

        return new AccessInterceptor();

    }

   

    /**

     *

     * Description:公務車APP端登陸攔截器

     * @return

     */

    @Bean

    public OfficalLoginInterceptor officalLoginInterceptor(){

        return new OfficalLoginInterceptor();

    }

    

    /**

     *

     * Description:APP端SessionListener

     * @return

     */

    @Bean

    public SessionListener sessionListener() {

        return new SessionListener();

    }

    

    @Bean

    public LoginTimeOutInterceptor loginTimeOutInterceptor(){

     return new LoginTimeOutInterceptor();

    }

    


    /**

     * 1.這裏的攔截器在容器初始化時加載在 HandlerExecutionChain 的 interceptors 變量裏

     * 2.interceptors 先加載用戶定義的攔截器,再加載 SpringMVC 默認的攔截器

     * 3.用戶定義的攔截器數組數據標號跟下面的順序一致。pre前處理是正序來處理;post後處理是按倒序來處理

     */

    @Override

    public void addInterceptors(InterceptorRegistry registry){

     registry.addInterceptor((HandlerInterceptor) returnMessageInterceptor());

//     registry.addInterceptor((HandlerInterceptor) returnMessageTimeHandlerInterceptor());

     registry.addInterceptor((HandlerInterceptor) loginInterceptor());

     registry.addInterceptor((HandlerInterceptor) accessInterceptor());

//     registry.addInterceptor((HandlerInterceptor) appLoginInterceptor());

     registry.addInterceptor((HandlerInterceptor) officalLoginInterceptor());

    

     registry.addInterceptor((HandlerInterceptor) loginTimeOutInterceptor());

    }

    

    

    

@Bean

public TaskScheduler scheduledExecutorService() {

ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();

scheduler.setPoolSize(8);

scheduler.setThreadNamePrefix("scheduled-thread-");

return scheduler;

}


}

登陸成功緩存加入登陸最近時間LoginController

/**

 * 登陸成功在緩存中寫入最新登陸時間

 */

SimpleCacheMgr cm= SimpleCacheMgr.getInstance();

cm.addCache(name, new Date().getTime());

 

測試--成功

登陸

登陸成功,同時控制檯打印有如下語句,說明登陸成功後增長了緩存緩存

10秒內隨便訪問一個鏈接

控制檯打印以下cookie

10秒後再次訪問

沒有返回由於已經被攔截,控制檯打印以下session

若在10秒內再次訪問--頁面顯示和控制檯打印內容跟10秒內第一次訪問相同,而且過時時間會順延10秒

代碼解讀

這個簡單的緩存實現本質上是個系統全局的HashMap<String,Long>()

過時時間如今是固定寫爲了10秒(攔截器中寫死了),能夠寫在配置文件中進行配置而後應用時獲取

如今這個簡單的緩存實際上只有一個功能,就是根據登陸用戶名保存該用戶名最近登陸時間,若是保存別的不一樣內容,那麼key重複的話怎麼解決?

由於緩存的key、value都是用的Object,很簡單的一個方法就是直接用HashMap<String,HashMap<String,Long>>()這樣的結構存儲用戶過時時間,key起名爲」longinOutTime」,取的時候就根據key名爲」longinOutTime」獲取HashMap<String,Long>結構,而後再在HashMap<String,Long>結構中存儲每一個用戶名的過時時間。app

若是存別的數據,好比菜單,那麼key起名爲」menu」。value根據實際狀況存儲便可。框架

固然,這只是個思路而已,很low,實際項目不會那麼幹。並且受HashMap大小限制,存儲數據是有限的,應用中若是要存儲的數據量過大,就會爆掉。分佈式

只是跟隨單例的服務的,沒法多個服務實例共享,沒法支持分佈式

緩存代碼改進(參考網頁中的代碼)

緩存管理類代碼

package com.richfit.ruiche.common.cache;


import java.util.Date;

import java.util.HashMap;

import java.util.HashSet;

import java.util.Iterator;

import java.util.Map;

import java.util.Set;


/**

 * 緩存管理類

 * 相對於 SimpleCacheMgr 增長了緩存屬性參數,會根據屬性參數的值按期清理緩存

 * @author Administrator

 *

 */

public class CacheMgr {


private static Map cacheMap = new HashMap();

private static Map cacheConfMap = new HashMap();


private static CacheMgr cm = null;


// 構造方法

private CacheMgr() {

}


public static CacheMgr getInstance() {

if (cm == null) {

cm = new CacheMgr();

Thread t = new ClearCache();

t.start();

}

return cm;

}


/**

 * 增長緩存

 *

 * @param key

 * @param value

 * @param ccm

 *            緩存對象

 * @return

 */

public boolean addCache(Object key, Object value, CacheConfModel ccm) {

System.out.println("開始增長緩存-------------");

boolean flag = false;

try {

cacheMap.put(key, value);

cacheConfMap.put(key, ccm);

System.out.println("增長緩存結束-------------");

System.out.println("now addcache==" + cacheMap.size());

flag = true;

} catch (Exception e) {

e.printStackTrace();

}


return flag;

}


/**

 * 獲取緩存實體

 */

public Object getValue(String key) {

Object ob = cacheMap.get(key);

if (ob != null) {

return ob;

} else {

return null;

}

}


/**

 * 獲取緩存數據的數量

 *

 * @return

 */

public int getSize() {

return cacheMap.size();

}


/**

 * 刪除緩存

 *

 * @param key

 * @return

 */

public boolean removeCache(Object key) {

boolean flag = false;

try {

cacheMap.remove(key);

cacheConfMap.remove(key);

flag = true;

} catch (Exception e) {

e.printStackTrace();

}

return flag;

}


/**

 * 清除緩存的類 繼承Thread線程類

 */

private static class ClearCache extends Thread {

public void run() {

while (true) {

Set tempSet = new HashSet();

Set set = cacheConfMap.keySet();

Iterator it = set.iterator();

while (it.hasNext()) {

Object key = it.next();

CacheConfModel ccm = (CacheConfModel) cacheConfMap.get(key);

// 比較是否須要清除

if (!ccm.isForever()) {

if ((new Date().getTime() - ccm.getBeginTime()) >= ccm

.getDurableTime() * 60 * 1000) {

// 能夠清除,先記錄下來

tempSet.add(key);

}

}

}

// 真正清除

Iterator tempIt = tempSet.iterator();

while (tempIt.hasNext()) {

Object key = tempIt.next();

cacheMap.remove(key);

cacheConfMap.remove(key);


}

System.out.println("now thread================>"

+ cacheMap.size());

// 休息

try {

Thread.sleep(60 * 1000L);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}


}

緩存屬性類代碼

package com.richfit.ruiche.common.cache;


/**

 * 緩存屬性類

 *

 * @author Administrator

 *

 */

public class CacheConfModel implements java.io.Serializable {


/**

 *

 */

private static final long serialVersionUID = 1L;

private long beginTime;// 緩存開始時間

private boolean isForever = false;// 是否持久

private int durableTime;// 持續時間


public long getBeginTime() {

return beginTime;

}


public void setBeginTime(long beginTime) {

this.beginTime = beginTime;

}


public boolean isForever() {

return isForever;

}


public void setForever(boolean isForever) {

this.isForever = isForever;

}


public int getDurableTime() {

return durableTime;

}


public void setDurableTime(int durableTime) {

this.durableTime = durableTime;

}


}

代碼解讀

每次增長緩存數據時會帶上如下配置屬性:

private long beginTime;// 緩存開始時間

private boolean isForever = false;// 是否持久

private int durableTime;// 持續時間

而且緩存小框架帶有按期清除過時緩存的功能。結合上面例子,能夠將是否持久設置爲false,持續時間根據項目實際需求設置,每次訪問均可在攔截器中更新緩存開始時間。這樣,到了設置的持續時間,緩存小框架就自動把過時緩存自動清理掉了。

具體的還要實際調試代碼,看是否有須要改進的地方。

相關文章
相關標籤/搜索