文档章节

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

k
 karma123
发布于 2018/06/27 20:20
字数 2270
阅读 53
收藏 2

参考网页

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

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

© 著作权归作者所有

k
粉丝 7
博文 94
码字总数 68903
作品 0
丰台
私信 提问
小程序开发-梳理登录流程-v1.0

最近发现小程序的登录逻辑还有一些新的心得,所以记录一下。 一、官方微信小程序登录流程个人理解 在小程序官网里面会提到一个小程序的登录逻辑,这是官方推荐的登录逻辑,也就是所谓的小程序...

线上猛如虎_线下怂如鼠
2018/10/22
0
0
PHP中解决ajax请求session过期退出登录问题

PHP判断一个请求是Ajax请求还是普通请求 1、session过期,如果直接是url请求,或者用户在打开的系统页面中直接清除缓存及cookie信息,可直接在php的入口文件中调用以下封装的方法,进行sessi...

Junn
2013/08/05
0
0
Volley学习笔记(二)

上一篇介绍了Volley.java、RequestQueue.java、Request.java三个类,其中RequestQueue.java里面讲道了两个队列,一个缓存队列一个网络请求队列。 CacheDispatcher.java 这个类用于处理调度走...

tomcater
2016/05/25
33
0
分布式缓存组件 - xcache

项目介绍 微服务时代,我们一般会用分布式缓存在提高系统的并发能力。 例如使用memcached、redis等比较知名的。但是很多时候, 作为普通业务程序员,他们可能在写业务的时候,并不想关心底层...

徐安是个好人
2018/06/25
0
0
cookie+memcached实现单点登陆

10年的时候在iteye的第一篇文章记录了一下当时怎么实现我们系统的单点登陆。不过那个时候文章写的不好,思路也很浮躁,很难看懂,在csdn的第一篇技术博客打算重新温顾一下当时实现单点登陆的...

吞吞吐吐的
2017/10/18
0
0

没有更多内容

加载失败,请刷新页面

加载更多

适合钱包应用开发的ERC20代币数据集

Erc20Tokens数据集包含超过1000种主流的以太坊ERC20代币的描述数据清单和图标,可用于钱包等区块链应用的开发,支持使用Java、Python、Php、NodeJs、C#等各种开发语言查询主流ERC20代币的相关...

汇智网教程
35分钟前
1
0
micro微服务 基础组件的组织方式

micro微服务 基础组件的组织方式 简介 micro是go语言实现的一个微服务框架,该框架自身实现了为服务常见的几大要素,网关,代理,注册中心,消息传递,也支持可插拔扩展。本本通过micro中的一...

魂祭心
今天
4
0
简单的博客系统(三)使用Django的后台管理功能

Django新建项目和应用后,自带有后台管理功能,可直接使用 创建后台管理员账户 (demosite) E:\PycharmProjects\demosite>python manage.py createsuperuserUsername: adminEmail address:...

ZeroBit
今天
3
0
The /usr/local/mysql/data directory is not owned by the 'mysql' to '_mysql' user

20190720 经过前两天折腾环境,重装了 apache 和 mysql 之后,今天调试程序是突然发现,本机的 mysql 起不来了! 在启动面板上,显示有这样一行小字 (抱歉!光顾着解决问题,没有记录下来图片...

wwzzhh166
今天
4
0
centos安装增强功能出现kernel headers not found for target kernel解决办法

最近新安装一个centos虚拟机,在安装增强功能的时候出现了,kernel headers not found for target kernel的错误。特记下我的解决方案。 1.update kernel yum update kernel -y 2.Install the...

mbzhong
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部