shiro session存redis

先看配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

<description>Shiro安全配置</description>
<context:property-placeholder ignore-unresolvable="true" location="classpath:cookie.properties"/>


<!-- 安全认证过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/common/login"/>
<property name="unauthorizedUrl" value="/error/403"/>
<property name="successUrl" value="/admins/indexed/index"></property>
<property name="filters">
<map>
<entry key="exec">
<bean class="com.mark.demo.shiro.security.filter.SimpleExecutiveFilter"/>
</entry>
<entry key="authc">
<bean class="com.mark.demo.shiro.security.filter.AuthenticationFilter">
<property name="rememberMeParam" value="rememberMe"/>
</bean>
</entry>
<entry key="roles">
<bean class="org.apache.shiro.web.filter.authz.RolesAuthorizationFilter"/>
</entry>
</map>
</property>
<property name="filterChainDefinitions">
<value>
/js/** = anon,exec
/css/** = anon,exec
/video/** = anon,exec
/upload/** = anon,exec
/uploads/** = anon,exec
/fonts/** = anon,exec
/getAuthPrice/** = anon,exec
/common/login/** = anon,exec
/common/forgotpass/** = anon,exec
/menu/** = authc,exec
/brand/** = authc,exec
/common/** = authc,exec
/admins/** = authc,exec
</value>
</property>
</bean>

<!-- 定义授权缓存管理器 -->
<bean id="shiroCacheManager" class="com.mark.demo.shiro.security.cache.RedisCacheManager"/>
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- SHIRO 认证匹配 -->
<bean id="userCredentialsMatcher" class="com.mark.demo.shiro.security.UserCredentialsMatcher"/>

<!-- 系统安全认证 -->
<bean id="userAuthorizingRealm" class="com.mark.demo.shiro.security.MysqlRealm">
<property name="credentialsMatcher" ref="userCredentialsMatcher"/>
<property name="sessionDAO" ref="sessionDAO"/>
<property name="userMapper" ref="userMapper"></property>
</bean>

<!-- 自定义会话 -->
<bean id="sessionDAO" class="com.mark.demo.shiro.security.session.RedisSessionDAO">
<property name="sessionIdGenerator">
<bean class="com.mark.demo.shiro.security.utils.IdGen"/>
</property>
</bean>

<!-- 指定本系统SESSIONID, 默认为: JSESSIONID -->
<bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg name="name" value="${cookie.session}"/>
<property name="domain" value="${cookie.domain}"/>
<property name="path" value="${cookie.path}"/>
<property name="httpOnly" value="true"/>
</bean>

<!-- 指定本系统REMEMBERID -->
<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg name="name" value="${cookie.remember}"/>
<property name="domain" value="${cookie.domain}"/>
<property name="path" value="${cookie.path}"/>
<property name="httpOnly" value="true"/>
<property name="maxAge" value="2592000"/>
</bean>

<!-- 自定义会话管理配置 -->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="sessionDAO" ref="sessionDAO"/>
<property name="globalSessionTimeout" value="1800000"/>
<property name="sessionValidationInterval" value="120000"/>
<property name="sessionValidationSchedulerEnabled" value="true"/>
<property name="sessionIdCookie" ref="sessionIdCookie"/>
<property name="sessionIdCookieEnabled" value="true"/>
</bean>

<!-- rememberMe管理器 -->
<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
<property name="cipherKey" value="#{T(org.apache.shiro.codec.Base64).decode('4AvVhmFLUs0KTA3Kprsdag==')}"/>
<property name="cookie" ref="rememberMeCookie"/>
</bean>

<!-- 定义Shiro安全管理配置 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="userAuthorizingRealm"/>
<property name="rememberMeManager" ref="rememberMeManager"/>
<property name="sessionManager" ref="sessionManager"/>
<property name="cacheManager" ref="shiroCacheManager"/>
<property name="sessionMode" value="http"></property>
</bean>

<!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
<property name="arguments" ref="securityManager"/>
</bean>

</beans>

主要就是实现

com.mark.demo.shiro.security.session.RedisSessionDAO

当session有更新时就会使用这个dao更新redis里的缓存

package com.mark.demo.shiro.security.session;

import java.util.Collection;

import org.apache.shiro.session.Session;


public interface CustomSessionDAO extends org.apache.shiro.session.mgt.eis.SessionDAO
{
/**
* 获取活动会话
* @param includeLeave 是否包括离线(最后访问时间大于3分钟为离线会话)
* @return
*/
Collection<Session> getActiveSessions(boolean includeLeave);

/**
* 获取活动会话
* @param includeLeave 是否包括离线(最后访问时间大于3分钟为离线会话)
* @param principal 根据登录者对象获取活动会话
* @param filterSession 不为空,则过滤掉(不包含)这个会话。
* @return
*/
Collection<Session> getActiveSessions(boolean includeLeave, Object principal, Session filterSession);
}
package com.mark.demo.shiro.security.session;

import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.Set;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Sets;
import com.mark.demo.shiro.entity.User;
import com.mark.demo.shiro.utils.JedisUtils;
import com.mark.demo.shiro.utils.StringUtils;


public class RedisSessionDAO extends AbstractSessionDAO implements CustomSessionDAO
{
private static final Logger logger = LoggerFactory.getLogger(RedisSessionDAO.class);

public static final String SESSION_GROUPS = "redis_shiro_session_group";

public static final String SESSION_PREFIX = "session_";

@Override
public void update(Session session) throws UnknownSessionException
{
if (session == null || session.getId() == null) { return; }
String key = String.valueOf(SESSION_PREFIX + session.getId());
int timeoutSeconds = (int) (session.getTimeout() / 1000);
JedisUtils.setObject(key, session, timeoutSeconds);
User principal = null;
SimplePrincipalCollection collection = (SimplePrincipalCollection) session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
if (null != collection)
{
principal = (User) collection.getPrimaryPrincipal();
}

String principalId = principal != null ? principal.getId()+"": StringUtils.EMPTY;
JedisUtils.setMapField(SESSION_GROUPS, key, principalId + "|" + session.getTimeout() + "|" + session.getLastAccessTime().getTime());

}

/**
* 清空会话及缓存
*/
public static void clean()
{
Map<String, String> map=JedisUtils.getMap(SESSION_GROUPS);
JedisUtils.del(SESSION_GROUPS);
for (Map.Entry<String, String> entry : map.entrySet())
{
JedisUtils.del(entry.getKey());
if (logger.isDebugEnabled())
{
logger.debug("remove session {} ", entry.getKey());
}
}

}

@Override
public void delete(Session session)
{
if (session == null || session.getId() == null) { return; }
String key = String.valueOf(SESSION_PREFIX + session.getId());
JedisUtils.removeMapField(SESSION_GROUPS,key);
JedisUtils.del(key);

}

@Override
public Collection<Session> getActiveSessions()
{
return getActiveSessions(true);
}

/**
* 获取活动会话
* @param includeLeave 是否包括离线(最后访问时间大于3分钟为离线会话)
* @return
*/
@Override
public Collection<Session> getActiveSessions(boolean includeLeave)
{
return getActiveSessions(includeLeave, null, null);
}

/**
* 获取活动会话
* @param includeLeave 是否包括离线(最后访问时间大于3分钟为离线会话)
* @param principal 根据登录者对象获取活动会话
* @param filterSession 不为空,则过滤掉(不包含)这个会话。
* @return
*/
@Override
public Collection<Session> getActiveSessions(boolean includeLeave, Object principal, Session filterSession)
{
Set<Session> sessions = Sets.newHashSet();
try
{
Map<String, String> map =JedisUtils.getMap(SESSION_GROUPS);
if(map==null){
return sessions;
}
for (Map.Entry<String, String> e : map.entrySet())
{
if (StringUtils.isNotBlank(e.getKey()) && StringUtils.isNotBlank(e.getValue()))
{
String[] ss = StringUtils.split(e.getValue(), "|");
if (ss != null && ss.length == 3)
{
SimpleSession session = new SimpleSession();
session.setId(e.getKey());
session.setAttribute("principalId", ss[0]);
session.setTimeout(Long.valueOf(ss[1]));
session.setLastAccessTime(new Date(Long.valueOf(ss[2])));
try
{
// 验证SESSION
session.validate();
boolean isActiveSession = false;
// 不包括离线并符合最后访问时间小于等于3分钟条件。
if (includeLeave )
{
isActiveSession = true;
}
// 过滤掉的SESSION
if (filterSession != null && filterSession.getId().equals(session.getId()))
{
isActiveSession = false;
}
if (isActiveSession)
{
sessions.add(session);
}
}
// SESSION验证失败
catch (Exception e2)
{
JedisUtils.removeMapField(SESSION_GROUPS, e.getKey());
JedisUtils.del(e.getKey());
}
}
// 存储的SESSION不符合规则
else
{
JedisUtils.removeMapField(SESSION_GROUPS, e.getKey());
JedisUtils.del(e.getKey());
}
}
// 存储的SESSION无Value
else if (StringUtils.isNotBlank(e.getKey()))
{
JedisUtils.removeMapField(SESSION_GROUPS, e.getKey());
JedisUtils.del(e.getKey());
}
}
logger.info("getActiveSessions size: {} ", sessions.size());
}
catch (Exception e)
{
logger.error("getActiveSessions", e);
}

return sessions;
}

@Override
protected Serializable doCreate(Session session)
{
Serializable sessionId = this.generateSessionId(session);
this.assignSessionId(session, sessionId);
this.update(session);
// SecurityUtils.getSubject()
return sessionId;
}

@Override
protected Session doReadSession(Serializable sessionId)
{
Session session = null;
try
{
String key = String.valueOf(SESSION_PREFIX + sessionId);
session = (Session) JedisUtils.getObject(key);
}
catch (Exception e)
{
logger.error("doReadSession {} {}", sessionId, e);
}

return session;
}

@Override
public Session readSession(Serializable sessionId) throws UnknownSessionException
{
try
{
return super.readSession(sessionId);
}
catch (UnknownSessionException e)
{
return null;
}
}
}

demo地址:https://github.com/13567436138/shiro-demo