redis shiro 手动剔除某个用户

这里简单做了点尝试估计还是有漏洞,而且逻辑有些复杂
以后再做个思路简单的

这里的设计思路:每个用户在数据库里面都有一个UserId
我们就在这个UserId上面做文章
每个用户登录成功后就会用自己的UserId做为key,这次登录的SessionId作为value存储一对键值对到redis中这个我们可以靠securitymanage管理的SessionDao来管理
下面我把我的shiroConfig贴出来

package com.crsri.config;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.Filter;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.session.mgt.eis.SessionIdGenerator;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.mindrot.jbcrypt.BCrypt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.crsri.realm.LoginRealm;
import com.crsri.service.system.MenuService;
import com.crsri.util.PropertiesUtils;

@Configuration
public class ShiroConfiguration {
@Autowired
private MenuService menuService;
@Value(" s p r i n g . r e d i s . h o s t " ) p r i v a t e S t r i n g h o s t ; @ V a l u e ( " {spring.redis.host}") private String host; @Value(" spring.redis.host")privateStringhost;@Value("{spring.redis.port}")
private int port;
//@Value("${spring.redis.password}")
//private int password;
/**
* shiro权限配置
*
* @param securityManager
* @return
*/
@Bean(name = “shiroFilter”)
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {

	/*LinkedHashMap<String, Filter> filtsMap=new LinkedHashMap<String, Filter>();*/
   /* filtsMap.put("perms",new MyPermsFilter())*/;
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    shiroFilterFactoryBean.setSecurityManager(securityManager);
    //手机端ajax判断
	 Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
	 filters.put("authc", new ShiroLoginFilter());
   /* shiroFilterFactoryBean.setFilters(filters);*/
	shiroFilterFactoryBean.setLoginUrl("/login.html"); 
   /* shiroFilterFactoryBean.setSuccessUrl("/index.html");*/
    /*shiroFilterFactoryBean.setUnauthorizedUrl("/403.html");*/
    /*shiroFilterFactoryBean.setFilters(filtsMap);*/
    Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
    filterChainDefinitionMap.put("/plugin/**", "anon");
    filterChainDefinitionMap.put("/css/system/main.css", "anon");
    filterChainDefinitionMap.put("/pageoffice.js", "anon");
    filterChainDefinitionMap.put("/jquery.min.js", "anon");
    filterChainDefinitionMap.put("/login2.html", "anon");
    filterChainDefinitionMap.put("/login1.html", "anon");
    filterChainDefinitionMap.put("/static/**", "anon");
    filterChainDefinitionMap.put("/library/**", "anon");
    filterChainDefinitionMap.put("/js/**", "anon");
    filterChainDefinitionMap.put("/img/**", "anon");
    filterChainDefinitionMap.put("/tologin/**", "anon");
    filterChainDefinitionMap.put("/login", "anon");
    filterChainDefinitionMap.put("/login1", "anon");
    filterChainDefinitionMap.put("/getcode", "anon");
    filterChainDefinitionMap.put("/oauth/**", "anon");
    filterChainDefinitionMap.put("/api/**", "anon");
    filterChainDefinitionMap.put("/api-login/**", "anon");
   /* filterChainDefinitionMap.put("/**", "roles[liufei1]");*/
    List<Map<String, Object>> perms = menuService.PermsInit();// 初始化权限
    if(perms!=null) {
    	for (Map<String, Object> m : perms) {
    		if(((String)m.get("Url")).length()!=0) {
    			//System.out.println("url--------------"+m.get("Url"));
    			//System.out.println("perms---------------"+m.get("Perms"));
    			filterChainDefinitionMap.put((String) m.get("Url"), "perms["+(String)m.get("Perms") + "]");
    		}
    	}
    }
    filterChainDefinitionMap.put("/**", "authc");
    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
    return shiroFilterFactoryBean;
}

/**
 * 配置核心安全事务管理器
 * 
 * @return
 */
@Bean(name = "securityManager")
public SecurityManager securityManager() {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    // 设置自定义realm.
    securityManager.setRealm(securityRealm());
    // 配置记住我
    securityManager.setRememberMeManager(rememberMeManager());
    // 配置redis缓存
    securityManager.setCacheManager(cacheManager());
    // 配置自定义session管理,使用redis
    securityManager.setSessionManager(sessionManager());
    return securityManager;
}

/**
 * Shiro生命周期处理器
 */
@Bean(name = "lifecycleBeanPostProcessor")
public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
    return new LifecycleBeanPostProcessor();
}

@Bean
public Realm securityRealm() {
    // TODO Auto-generated method stub
    LoginRealm loginRealm = new LoginRealm();
    loginRealm.setCredentialsMatcher(new CredentialsMatcher() {// 加密认证
        // @Override
        public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
            // TODO Auto-generated method stub
            UsernamePasswordToken userToken = (UsernamePasswordToken) token;
            // 要验证的明文密码
            String plaintext = new String(userToken.getPassword());
            // 数据库中加密后的密文
            String hashed = info.getCredentials().toString();// 获取密码数据库中用户加密密码
            return BCrypt.checkpw(plaintext, hashed);
        }
    });
    // 启用身份验证缓存,即缓存AuthenticationInfo信息,默认false
    /*loginRealm.setAuthenticationCachingEnabled(true);*/
    // 缓存AuthenticationInfo信息的缓存名称 在ehcache-shiro.xml中有对应缓存的配置
    /*loginRealm.setAuthenticationCacheName("authenticationCache");*/
    // 启用授权缓存,即缓存AuthorizationInfo信息,默认false
    loginRealm.setAuthorizationCachingEnabled(true);
    // 缓存AuthorizationInfo信息的缓存名称 在ehcache-shiro.xml中有对应缓存的配置
    loginRealm.setAuthorizationCacheName("authorizationCache");

    return loginRealm;
}


/**
 * cookie对象;会话Cookie模板 ,默认为: JSESSIONID 问题:
 * 与SERVLET容器名冲突,重新定义为sid或rememberMe,自定义
 * 
 * @return
 */
@Bean
public SimpleCookie rememberMeCookie() {
    // 这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
    SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
    // setcookie的httponly属性如果设为true的话,会增加对xss防护的安全系数。它有以下特点:
    // setcookie()的第七个参数
    // 设为true后,只能通过http访问,javascript无法访问
    // 防止xss读取cookie
    simpleCookie.setHttpOnly(true);
    simpleCookie.setPath("/");
    // <!-- 记住我cookie生效时间30天 ,单位秒;-->
    simpleCookie.setMaxAge(2592000);
    return simpleCookie;
}

/**
 * cookie管理对象;记住我功能,rememberMe管理器
 * 
 * @return
 */
@Bean
public CookieRememberMeManager rememberMeManager() {
    CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
    cookieRememberMeManager.setCookie(rememberMeCookie());
    // rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
    cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
    return cookieRememberMeManager;
}

/**
 * FormAuthenticationFilter 过滤器 过滤记住我
 * 
 * @return
 */
@Bean
public FormAuthenticationFilter formAuthenticationFilter() {
    FormAuthenticationFilter formAuthenticationFilter = new FormAuthenticationFilter();
    // 对应前端的checkbox的name = rememberMe
    formAuthenticationFilter.setRememberMeParam("rememberMe");
    return formAuthenticationFilter;
}

/**
 * shiro缓存管理器; 需要添加到securityManager中
 * 
 * @return
 */
@Bean
public RedisCacheManager cacheManager() {
    RedisCacheManager redisCacheManager = new RedisCacheManager();
    redisCacheManager.setRedisManager(redisManager());
    // redis中针对不同用户缓存
    redisCacheManager.setPrincipalIdFieldName("username");
    // 用户权限信息缓存时间
    redisCacheManager.setExpire(3600);
    return redisCacheManager;
}


/**
 * 让某个实例的某个方法的返回值注入为Bean的实例 Spring静态注入
 * 
 * @return
 */
@Bean
public MethodInvokingFactoryBean getMethodInvokingFactoryBean() {
    MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean();
    factoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
    factoryBean.setArguments(new Object[] { securityManager() });
    return factoryBean;
}

/**
 * 配置session监听
 * 
 * @return
 */
@Bean("sessionListener")
public ShiroSessionListener sessionListener() {
    ShiroSessionListener sessionListener = new ShiroSessionListener();
    return sessionListener;
}

/**
 * 配置会话ID生成器
 * 
 * @return
 */
@Bean
public SessionIdGenerator sessionIdGenerator() {
    return new JavaUuidSessionIdGenerator();
}

@Bean
public RedisManager redisManager() {
    RedisManager redisManager = new RedisManager();
    redisManager.setHost(host);
    redisManager.setPort(port);
    // redisManager.setPassword("123456");
    return redisManager;
}

/**
 * SessionDAO的作用是为Session提供CRUD并进行持久化的一个shiro组件 MemorySessionDAO 直接在内存中进行会话维护
 * EnterpriseCacheSessionDAO
 * 提供了缓存功能的会话维护,默认情况下使用MapCache实现,内部使用ConcurrentHashMap保存缓存的会话。
 * 
 * @return
 */
@Bean
public SessionDAO sessionDAO() {
	RedisSessionKey redisSessionDAO = new RedisSessionKey();
	//RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
    redisSessionDAO.setRedisManager(redisManager());
    // session在redis中的保存时间,最好大于session会话超时时间
    redisSessionDAO.setExpire(1800);
    return redisSessionDAO;
}

/**
 * 配置保存sessionId的cookie 注意:这里的cookie 不是上面的记住我 cookie 记住我需要一个cookie session管理
 * 也需要自己的cookie 默认为: JSESSIONID 问题: 与SERVLET容器名冲突,重新定义为sid
 * 
 * @return
 */
@Bean("sessionIdCookie")
public SimpleCookie sessionIdCookie() {
    // 这个参数是cookie的名称
    SimpleCookie simpleCookie = new SimpleCookie("sid");
    // setcookie的httponly属性如果设为true的话,会增加对xss防护的安全系数。它有以下特点:

    // setcookie()的第七个参数
    // 设为true后,只能通过http访问,javascript无法访问
    // 防止xss读取cookie
    simpleCookie.setHttpOnly(true);
    simpleCookie.setPath("/");
    // maxAge=-1表示浏览器关闭时失效此Cookie
    simpleCookie.setMaxAge(-1);
    return simpleCookie;
}

/**
 * 配置会话管理器,设定会话超时及保存
 * 
 * @return
 */
@Bean("sessionManager")
public SessionManager sessionManager() {
    DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
    Collection<SessionListener> listeners = new ArrayList<SessionListener>();
    // 配置监听
    listeners.add(sessionListener());
    sessionManager.setSessionListeners(listeners);
    sessionManager.setSessionIdCookie(sessionIdCookie());
    sessionManager.setSessionDAO(sessionDAO());
    sessionManager.setCacheManager(cacheManager());

    // 全局会话超时时间(单位毫秒),默认30分钟 暂时设置为2小时用来测试
    sessionManager.setGlobalSessionTimeout(1800000 * 4);
    // 是否开启删除无效的session对象 默认为true
    sessionManager.setDeleteInvalidSessions(true);
    // 是否开启定时调度器进行检测过期session 默认为true
    sessionManager.setSessionValidationSchedulerEnabled(true);
    // 设置session失效的扫描时间, 清理用户直接关闭浏览器造成的孤立会话 默认为 1个小时
    // 设置该属性 就不需要设置 ExecutorServiceSessionValidationScheduler
    // 底层也是默认自动调用ExecutorServiceSessionValidationScheduler
    sessionManager.setSessionValidationInterval(3600000);
    // 取消url 后面的 JSESSIONID
    sessionManager.setSessionIdUrlRewritingEnabled(false);
    return sessionManager;

}

/**
 * 开启shiro 注解模式 可以在controller中的方法前加上注解 如 @RequiresPermissions("getProjectName:add")
 * 
 * @param securityManager
 * @return
 */
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
        @Qualifier("securityManager") SecurityManager securityManager) {
    AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
    authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
    return authorizationAttributeSourceAdvisor;
}

@Bean
public ServletRegistrationBean servletRegistrationBean() {
	com.zhuozhengsoft.pageoffice.poserver.Server poserver = new com.zhuozhengsoft.pageoffice.poserver.Server();
	String poSysPath = PropertiesUtils.getBaseFilePath() + File.separator + "lic" + File.separator;
	File registerDir = new File(poSysPath);
	if (!registerDir.exists()) {
		registerDir.mkdirs();
	}
	poserver.setSysPath(poSysPath);//设置PageOffice注册成功后,license.lic文件存放的目录
	ServletRegistrationBean srb = new ServletRegistrationBean(poserver);
	srb.addUrlMappings("/poserver.zz");
	srb.addUrlMappings("/posetup.exe");
	srb.addUrlMappings("/jquery.min.js");
	srb.addUrlMappings("/pobstyle.css");
	srb.addUrlMappings("/pageoffice.js");
	srb.addUrlMappings("/sealsetup.exe");
    return srb;// 
}

}

核心代码在这里:
secutrityManget管理SessionManage
在这里插入图片描述
然后SessionManager管理SessionDao
在这里插入图片描述
然后SessionDao里面new一个RedisSessionDAO 然后来管理redisManger
在这里插入图片描述
这里是redisManager
在这里插入图片描述
这样我们的session就完全交给了redis来管理,这里以后我还要多看下源码,怎么交给的,暂时不懂

我们这里在这里做下手脚
RedisSessionDAO
这里进去我们可以看到代码是这样
package org.crazycake.shiro;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.crazycake.shiro.exception.SerializationException;
import org.crazycake.shiro.serializer.ObjectSerializer;
import org.crazycake.shiro.serializer.RedisSerializer;
import org.crazycake.shiro.serializer.StringSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.*;

public class RedisSessionDAO extends AbstractSessionDAO {

private static Logger logger = LoggerFactory.getLogger(RedisSessionDAO.class);

private static final String DEFAULT_SESSION_KEY_PREFIX = "shiro:session:";
private String keyPrefix = DEFAULT_SESSION_KEY_PREFIX;

private static final long DEFAULT_SESSION_IN_MEMORY_TIMEOUT = 1000L;
/**
 * doReadSession be called about 10 times when login.
 * Save Session in ThreadLocal to resolve this problem. sessionInMemoryTimeout is expiration of Session in ThreadLocal.
 * The default value is 1000 milliseconds (1s).
 * Most of time, you don't need to change it.
 */
private long sessionInMemoryTimeout = DEFAULT_SESSION_IN_MEMORY_TIMEOUT;

// expire time in seconds
private static final int DEFAULT_EXPIRE = -2;
private static final int NO_EXPIRE = -1;

/**
 * Please make sure expire is longer than sesion.getTimeout()
 */
private int expire = DEFAULT_EXPIRE;

private static final int MILLISECONDS_IN_A_SECOND = 1000;

private IRedisManager redisManager;
private RedisSerializer keySerializer = new StringSerializer();
private RedisSerializer valueSerializer = new ObjectSerializer();
private static ThreadLocal sessionsInThread = new ThreadLocal();

@Override
public void update(Session session) throws UnknownSessionException {
	this.saveSession(session);
}

/**
 * save session
 * @param session
 * @throws UnknownSessionException
 */
private void saveSession(Session session) throws UnknownSessionException {
	if (session == null || session.getId() == null) {
		logger.error("session or session id is null");
		throw new UnknownSessionException("session or session id is null");
	}
	byte[] key;
	byte[] value;
	try {
		key = keySerializer.serialize(getRedisSessionKey(session.getId()));
		value = valueSerializer.serialize(session);
	} catch (SerializationException e) {
		logger.error("serialize session error. session id=" + session.getId());
		throw new UnknownSessionException(e);
	}
	if (expire == DEFAULT_EXPIRE) {
		this.redisManager.set(key, value, (int) (session.getTimeout() / MILLISECONDS_IN_A_SECOND));
		return;
	}
	if (expire != NO_EXPIRE && expire * MILLISECONDS_IN_A_SECOND < session.getTimeout()) {
		logger.warn("Redis session expire time: "
				+ (expire * MILLISECONDS_IN_A_SECOND)
				+ " is less than Session timeout: "
				+ session.getTimeout()
				+ " . It may cause some problems.");
	}
	this.redisManager.set(key, value, expire);
}

@Override
public void delete(Session session) {
	if (session == null || session.getId() == null) {
		logger.error("session or session id is null");
		return;
	}
	try {
		redisManager.del(keySerializer.serialize(getRedisSessionKey(session.getId())));
	} catch (SerializationException e) {
		logger.error("delete session error. session id=" + session.getId());
	}
}

@Override
public Collection<Session> getActiveSessions() {
	Set<Session> sessions = new HashSet<Session>();
	try {
		Set<byte[]> keys = redisManager.keys(this.keySerializer.serialize(this.keyPrefix + "*"));
		if (keys != null && keys.size() > 0) {
			for (byte[] key:keys) {
				Session s = (Session) valueSerializer.deserialize(redisManager.get(key));
				sessions.add(s);
			}
		}
	} catch (SerializationException e) {
		logger.error("get active sessions error.");
	}
	return sessions;
}

@Override
protected Serializable doCreate(Session session) {
	if (session == null) {
		logger.error("session is null");
		throw new UnknownSessionException("session is null");
	}
	Serializable sessionId = this.generateSessionId(session);  
    this.assignSessionId(session, sessionId);
    this.saveSession(session);
	return sessionId;
}

@Override
protected Session doReadSession(Serializable sessionId) {
	if (sessionId == null) {
		logger.warn("session id is null");
		return null;
	}
	Session s = getSessionFromThreadLocal(sessionId);

	if (s != null) {
		return s;
	}

	logger.debug("read session from redis");
	try {
		s = (Session) valueSerializer.deserialize(redisManager.get(keySerializer.serialize(getRedisSessionKey(sessionId))));
		setSessionToThreadLocal(sessionId, s);
	} catch (SerializationException e) {
		logger.error("read session error. settionId=" + sessionId);
	}
	return s;
}

private void setSessionToThreadLocal(Serializable sessionId, Session s) {
	Map<Serializable, SessionInMemory> sessionMap = (Map<Serializable, SessionInMemory>) sessionsInThread.get();
	if (sessionMap == null) {
        sessionMap = new HashMap<Serializable, SessionInMemory>();
        sessionsInThread.set(sessionMap);
    }
	SessionInMemory sessionInMemory = new SessionInMemory();
	sessionInMemory.setCreateTime(new Date());
	sessionInMemory.setSession(s);
	sessionMap.put(sessionId, sessionInMemory);
}

private Session getSessionFromThreadLocal(Serializable sessionId) {
	Session s = null;

	if (sessionsInThread.get() == null) {
		return null;
	}

	Map<Serializable, SessionInMemory> sessionMap = (Map<Serializable, SessionInMemory>) sessionsInThread.get();
	SessionInMemory sessionInMemory = sessionMap.get(sessionId);
	if (sessionInMemory == null) {
		return null;
	}
	Date now = new Date();
	long duration = now.getTime() - sessionInMemory.getCreateTime().getTime();
	if (duration < sessionInMemoryTimeout) {
		s = sessionInMemory.getSession();
		logger.debug("read session from memory");
	} else {
		sessionMap.remove(sessionId);
	}

	return s;
}

private String getRedisSessionKey(Serializable sessionId) {
	return this.keyPrefix + sessionId;
}

public IRedisManager getRedisManager() {
	return redisManager;
}

public void setRedisManager(IRedisManager redisManager) {
	this.redisManager = redisManager;
}

public String getKeyPrefix() {
	return keyPrefix;
}

public void setKeyPrefix(String keyPrefix) {
	this.keyPrefix = keyPrefix;
}

public RedisSerializer getKeySerializer() {
	return keySerializer;
}

public void setKeySerializer(RedisSerializer keySerializer) {
	this.keySerializer = keySerializer;
}

public RedisSerializer getValueSerializer() {
	return valueSerializer;
}

public void setValueSerializer(RedisSerializer valueSerializer) {
	this.valueSerializer = valueSerializer;
}

public long getSessionInMemoryTimeout() {
	return sessionInMemoryTimeout;
}

public void setSessionInMemoryTimeout(long sessionInMemoryTimeout) {
	this.sessionInMemoryTimeout = sessionInMemoryTimeout;
}

public int getExpire() {
	return expire;
}

public void setExpire(int expire) {
	this.expire = expire;
}

}

我来继承一下它然后改造下它,
下面是我写的继承类RedisSessionKey
package com.crsri.config;

import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.crazycake.shiro.IRedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.crazycake.shiro.SessionInMemory;
import org.crazycake.shiro.exception.SerializationException;
import org.crazycake.shiro.serializer.ObjectSerializer;
import org.crazycake.shiro.serializer.RedisSerializer;
import org.crazycake.shiro.serializer.StringSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import com.crsri.domain.system.User;

public class RedisSessionKey extends RedisSessionDAO{

@Resource
private RedisTemplate<String, Object>redisTemplate;

private static Logger logger = LoggerFactory.getLogger(RedisSessionDAO.class);

private static final String DEFAULT_SESSION_KEY_PREFIX = "shiro:session:";
private String keyPrefix = DEFAULT_SESSION_KEY_PREFIX;

private static final long DEFAULT_SESSION_IN_MEMORY_TIMEOUT = 1000L;
/**
 * doReadSession be called about 10 times when login.
 * Save Session in ThreadLocal to resolve this problem. sessionInMemoryTimeout is expiration of Session in ThreadLocal.
 * The default value is 1000 milliseconds (1s).
 * Most of time, you don't need to change it.
 */
private long sessionInMemoryTimeout = DEFAULT_SESSION_IN_MEMORY_TIMEOUT;

// expire time in seconds
private static final int DEFAULT_EXPIRE = -2;
private static final int NO_EXPIRE = -1;

/**
 * Please make sure expire is longer than sesion.getTimeout()
 */
private int expire = DEFAULT_EXPIRE;

private static final int MILLISECONDS_IN_A_SECOND = 1000;

private IRedisManager redisManager;
private RedisSerializer keySerializer = new StringSerializer();
private RedisSerializer valueSerializer = new ObjectSerializer();
private static ThreadLocal sessionsInThread = new ThreadLocal();

@Override
public void update(Session session) throws UnknownSessionException {
	String userID = null;
	//通过session获取Principal对象
	PrincipalCollection principal =(PrincipalCollection)(session.getAttribute((DefaultSubjectContext.PRINCIPALS_SESSION_KEY)));
	//如果principal不为null,说明其现在可以转换成User
	if(principal!=null) {
		//根据session来获得sessionId的信息,将其转换成字符串
		String id = session.getId().toString();
		User user=(User)principal.getPrimaryPrincipal();
		userID = user.getUserID();
		//生成一个redis的key,value将其保存到redis里面,30分钟,之后每次改变的时候与sessionId同步保存30分钟
		/**if(redisTemplate.hasKey(userID)) {
			String sessionId = redisTemplate.opsForValue().get(userID).toString();
			if(!("0".equals(sessionId))) {
				this.redisTemplate.opsForValue().set(userID, id,1800,TimeUnit.SECONDS);
			}else {
				this.redisTemplate.opsForValue().set(userID, "0",1800,TimeUnit.SECONDS);
			}
		}else {
			this.redisTemplate.opsForValue().set(userID, id,1800,TimeUnit.SECONDS);
		}**/
		if(!(redisTemplate.hasKey(userID))){
			this.redisTemplate.opsForValue().set(userID, id,1800,TimeUnit.SECONDS);
		}
	}
	this.saveSession(session);
}

/**
 * save session
 * @param session
 * @throws UnknownSessionException
 */
private void saveSession(Session session) throws UnknownSessionException {
	if (session == null || session.getId() == null) {
		logger.error("session or session id is null");
		throw new UnknownSessionException("session or session id is null");
	}
	byte[] key;
	byte[] value;
	try {
		key = keySerializer.serialize(getRedisSessionKey(session.getId()));
		value = valueSerializer.serialize(session);
	} catch (SerializationException e) {
		logger.error("serialize session error. session id=" + session.getId());
		throw new UnknownSessionException(e);
	}
	if (expire == DEFAULT_EXPIRE) {
		this.redisManager.set(key, value, (int) (session.getTimeout() / MILLISECONDS_IN_A_SECOND));
		return;
	}
	if (expire != NO_EXPIRE && expire * MILLISECONDS_IN_A_SECOND < session.getTimeout()) {
		logger.warn("Redis session expire time: "
				+ (expire * MILLISECONDS_IN_A_SECOND)
				+ " is less than Session timeout: "
				+ session.getTimeout()
				+ " . It may cause some problems.");
	}
	this.redisManager.set(key, value, expire);
}

@Override
public void delete(Session session) {
	logger.info("*****redis删除session");
	if (session == null || session.getId() == null) {
		logger.error("session or session id is null");
		return;
	}
	try {
		redisManager.del(keySerializer.serialize(getRedisSessionKey(session.getId())));
	} catch (SerializationException e) {
		logger.error("delete session error. session id=" + session.getId());
	}
}

@Override
public Collection<Session> getActiveSessions() {
	Set<Session> sessions = new HashSet<Session>();
	try {
		Set<byte[]> keys = redisManager.keys(this.keySerializer.serialize(this.keyPrefix + "*"));
		if (keys != null && keys.size() > 0) {
			for (byte[] key:keys) {
				Session s = (Session) valueSerializer.deserialize(redisManager.get(key));
				sessions.add(s);
			}
		}
	} catch (SerializationException e) {
		logger.error("get active sessions error.");
	}
	return sessions;
}

@Override
protected Serializable doCreate(Session session) {
	logger.info("*****redis创建session");
	if (session == null) {
		logger.error("session is null");
		throw new UnknownSessionException("session is null");
	}
	Serializable sessionId = this.generateSessionId(session);  
    this.assignSessionId(session, sessionId);
    this.saveSession(session);
	return sessionId;
}

@Override
protected Session doReadSession(Serializable sessionId) {
	if (sessionId == null) {
		logger.warn("session id is null");
		return null;
	}
	Session s = getSessionFromThreadLocal(sessionId);

	if (s != null) {
		return s;
	}

	logger.debug("read session from redis");
	try {
		s = (Session) valueSerializer.deserialize(redisManager.get(keySerializer.serialize(getRedisSessionKey(sessionId))));
		setSessionToThreadLocal(sessionId, s);
	} catch (SerializationException e) {
		logger.error("read session error. settionId=" + sessionId);
	}
	return s;
}

private void setSessionToThreadLocal(Serializable sessionId, Session s) {
	Map<Serializable, SessionInMemory> sessionMap = (Map<Serializable, SessionInMemory>) sessionsInThread.get();
	if (sessionMap == null) {
        sessionMap = new HashMap<Serializable, SessionInMemory>();
        sessionsInThread.set(sessionMap);
    }
	SessionInMemory sessionInMemory = new SessionInMemory();
	sessionInMemory.setCreateTime(new Date());
	sessionInMemory.setSession(s);
	sessionMap.put(sessionId, sessionInMemory);
}

private Session getSessionFromThreadLocal(Serializable sessionId) {
	Session s = null;

	if (sessionsInThread.get() == null) {
		return null;
	}

	Map<Serializable, SessionInMemory> sessionMap = (Map<Serializable, SessionInMemory>) sessionsInThread.get();
	SessionInMemory sessionInMemory = sessionMap.get(sessionId);
	if (sessionInMemory == null) {
		return null;
	}
	Date now = new Date();
	long duration = now.getTime() - sessionInMemory.getCreateTime().getTime();
	if (duration < sessionInMemoryTimeout) {
		s = sessionInMemory.getSession();
		logger.debug("read session from memory");
	} else {
		sessionMap.remove(sessionId);
	}

	return s;
}

private String getRedisSessionKey(Serializable sessionId) {
	return this.keyPrefix + sessionId;
}

public IRedisManager getRedisManager() {
	return redisManager;
}

public void setRedisManager(IRedisManager redisManager) {
	this.redisManager = redisManager;
}

public String getKeyPrefix() {
	return keyPrefix;
}

public void setKeyPrefix(String keyPrefix) {
	this.keyPrefix = keyPrefix;
}

public RedisSerializer getKeySerializer() {
	return keySerializer;
}

public void setKeySerializer(RedisSerializer keySerializer) {
	this.keySerializer = keySerializer;
}

public RedisSerializer getValueSerializer() {
	return valueSerializer;
}

public void setValueSerializer(RedisSerializer valueSerializer) {
	this.valueSerializer = valueSerializer;
}

public long getSessionInMemoryTimeout() {
	return sessionInMemoryTimeout;
}

public void setSessionInMemoryTimeout(long sessionInMemoryTimeout) {
	this.sessionInMemoryTimeout = sessionInMemoryTimeout;
}

public int getExpire() {
	return expire;
}

public void setExpire(int expire) {
	this.expire = expire;
}

}

下面是核心代码
在这里插入图片描述
主要就是这做拦截后的初始化

然后我们看下手动删除的业务代码,这里禁止用户状态设置为‘0’,如果允许后就在redis中把这个key,value删掉

@Log(“更新用户”)
@RequestMapping(value="/updateUser",method=RequestMethod.PUT)
@ResponseBody
public ApiResponse updateUser(@RequestBody User user) {
if(ADMINID.equals(user.getUserID())) {
if(!(ADMINNAME.equals(user.getUsername()))) {
return ApiResponse.ofMsg(208,“admin名字不许被更改”);
}
}
if(user.getPassword()!=null && !"".equals(user.getPassword())){
String hashedPassword = Encryptiontools.encryptPassword(user.getPassword());//加密密码
user.setPassword(hashedPassword);//设置密文密码
};
userService.updateUserById(user);
Integer userStatus = user.getUserStatus();
String userID = user.getUserID();
Boolean hasKey = redisTemplate.hasKey(userID);
if(0==userStatus) {
//String sessionId = redisTemplate.opsForValue().get(userID).toString();
//redisTemplate.delete(“shiro:session:”+sessionId);
redisTemplate.opsForValue().set(userID, “0”,1800,TimeUnit.SECONDS);
}else {
if(hasKey) {
redisTemplate.delete(userID);
}
}
ShiroUtil.clearPermsCache();
setLogInfo(Update, “修改用户’”+user.getUsername()+"'密码成功",null);
return ApiResponse.ofMsg(200,“操作成功”);
}
核心代码是下面
在这里插入图片描述
这样我们的userId设置的key就指向value为0了
下面我们来重写下shiro的权限校验规则

下面
核心代码在刚刚的shiroconfigurantion里面
在这里插入图片描述
我们创建一个类继承UserFilter当然也可以继承大家喜欢用的AccessControlFilter
下面是改造后的代码
package com.crsri.config;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.UserFilter;
import org.springframework.data.redis.core.RedisTemplate;
import com.alibaba.fastjson.JSONObject;
import com.crsri.controller.response.ApiResponse;
import com.crsri.domain.system.User;
public class ShiroLoginFilter extends UserFilter{

private String sessionId;
private RedisTemplate<String, ?> redisTemplate;

@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
	// TODO Auto-generated method stub
	if (redisTemplate == null) {
	    redisTemplate = (RedisTemplate) ShiroSpringUtils.getBean("redisTemplate");
	}
	Subject subject = getSubject(request, response);
	boolean authenticated = subject.isAuthenticated();
	if (subject.isAuthenticated()) {
		User user =(User) (subject.getPrincipal());
		String userID = user.getUserID();
		sessionId = redisTemplate.opsForValue().get(userID).toString();
		if("0".equals(sessionId)) {
			return false;
		}
}
	return super.isAccessAllowed(request, response, mappedValue);
}

@Override
protected boolean onAccessDenied(ServletRequest request,ServletResponse response) throws Exception {
	HttpServletRequest req= (HttpServletRequest) request;
    HttpServletResponse httpServletResponse = (HttpServletResponse) response;
    if("0".equals(sessionId)) {
    	Subject subject = SecurityUtils.getSubject();
        subject.logout();
        //httpServletResponse.sendRedirect("/login.html");
    }
    if(isAjax(req)){//如果是ajax请求返回状态码
    	 httpServletResponse.setCharacterEncoding("UTF-8");
         httpServletResponse.setContentType("application/json");
         httpServletResponse.getWriter().write(JSONObject.toJSON(ApiResponse.ofMsg(403,"您还未登录,重新登录")).toString());
         httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
    }else{
    	/**
         * @Mark 非ajax请求重定向为登录页面
         */
    	 httpServletResponse.sendRedirect("/login.html");
    }
    return false;
}

public boolean isAjax(HttpServletRequest request){  
    String header = request.getHeader("X-Requested-With");  
    boolean isAjax = "XMLHttpRequest".equalsIgnoreCase(header) ? true:false;  
    return isAjax;  
}

}
核心代码解析:
在这里插入图片描述
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值