封装Map实现缓存并利用其判断登录是否过期

参考网页

https://blog.csdn.net/u012255097/article/details/53813998#commentBox

说明

例子是以自己在做的实际项目为例,单服务实例。

只是演示用,实际项目中不推荐使用。

单服务实例可以用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秒内随便访问一个连接

控制台打印如下

10秒后再次访问

没有返回因为已经被拦截,控制台打印如下

若在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>结构中存储每个用户名的过期时间。

如果存别的数据,比如菜单,那么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,持续时间根据项目实际需求设置,每次访问都可在拦截器中更新缓存开始时间。这样,到了设置的持续时间,缓存小框架就自动把过期缓存自动清理掉了。

具体的还要实际调试代码,看是否有需要改进的地方。

转载于:https://my.oschina.net/u/3866531/blog/1836659

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值