apache shiro集群实现(一) session共享
2016-03-17 17:45
246 查看
Apache Shiro的基本配置和构成这里就不详细说明了,其官网有说明文档,这里仅仅说明集群的解决方案,详细配置:shiro web config
Apache Shiro集群要解决2个问题,一个是session的共享问题,一个是授权信息的cache共享问题,官网给的例子是Ehcache的实现,在配置说明上不算很详细,我这里用nosql(redis)替代了ehcache做了session和cache的存储。
shiro spring的默认配置(单机,非集群)
[html]
view plaincopy
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="shiroDbRealm" />
<property name="cacheManager" ref="memoryConstrainedCacheManager" />
</bean>
<!-- 自定义Realm -->
<bean id="shiroDbRealm" class="com.xxx.security.shiro.custom.ShiroDbRealm">
<property name="credentialsMatcher" ref="customCredentialsMather"></property>
</bean>
<!-- 用户授权信息Cache(本机内存实现) -->
<bean id="memoryConstrainedCacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login" />
<property name="successUrl" value="/project" />
<property name="filterChainDefinitions">
<value>
/login = authc
/logout = logout
</value>
</property>
</bean>
[html]
view plain
copy
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="shiroDbRealm" />
<property name="cacheManager" ref="memoryConstrainedCacheManager" />
</bean>
<!-- 自定义Realm -->
<bean id="shiroDbRealm" class="com.xxx.security.shiro.custom.ShiroDbRealm">
<property name="credentialsMatcher" ref="customCredentialsMather"></property>
</bean>
<!-- 用户授权信息Cache(本机内存实现) -->
<bean id="memoryConstrainedCacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login" />
<property name="successUrl" value="/project" />
<property name="filterChainDefinitions">
<value>
/login = authc
/logout = logout
</value>
</property>
</bean>
上面的配置是shiro非集群下的配置,DefaultWebSecurityManager类不需要注入sessionManager属性,它会使用默认的sessionManager类,请看源码
[java]
view plaincopy
public DefaultWebSecurityManager() {
super();
((DefaultSubjectDAO) this.subjectDAO).setSessionStorageEvaluator(new DefaultWebSessionStorageEvaluator());
this.sessionMode = HTTP_SESSION_MODE;
setSubjectFactory(new DefaultWebSubjectFactory());
setRememberMeManager(new CookieRememberMeManager());
setSessionManager(new ServletContainerSessionManager());
}
[java]
view plain
copy
public DefaultWebSecurityManager() {
super();
((DefaultSubjectDAO) this.subjectDAO).setSessionStorageEvaluator(new DefaultWebSessionStorageEvaluator());
this.sessionMode = HTTP_SESSION_MODE;
setSubjectFactory(new DefaultWebSubjectFactory());
setRememberMeManager(new CookieRememberMeManager());
setSessionManager(new ServletContainerSessionManager());
}
在最后一行,set了默认的servlet容器实现的sessionManager,sessionManager会管理session的创建、删除等等。如果我们需要让session在集群中共享,就需要替换这个默认的sessionManager。在其官网上原话是这样的:
[html]
view plaincopy
Native Sessions
If you want your session configuration settings and clustering to be portable across servlet containers
(e.g. Jetty in testing, but Tomcat or JBoss in production), or you want to control specific session/clustering
features, you can enable Shiro's native session management.
The word 'Native' here means that Shiro's own enterprise session management implementation will be used to support
all Subject and HttpServletRequest sessions and bypass the servlet container completely. But rest assured - Shiro
implements the relevant parts of the Servlet specification directly so any existing web/http related code works as
expected and never needs to 'know' that Shiro is transparently managing sessions.
DefaultWebSessionManager
To enable native session management for your web application, you will need to configure a native web-capable
session manager to override the default servlet container-based one. You can do that by configuring an instance of
DefaultWebSessionManager on Shiro's SecurityManager.
[html]
view plain
copy
Native Sessions
If you want your session configuration settings and clustering to be portable across servlet containers
(e.g. Jetty in testing, but Tomcat or JBoss in production), or you want to control specific session/clustering
features, you can enable Shiro's native session management.
The word 'Native' here means that Shiro's own enterprise session management implementation will be used to support
all Subject and HttpServletRequest sessions and bypass the servlet container completely. But rest assured - Shiro
implements the relevant parts of the Servlet specification directly so any existing web/http related code works as
expected and never needs to 'know' that Shiro is transparently managing sessions.
DefaultWebSessionManager
To enable native session management for your web application, you will need to configure a native web-capable
session manager to override the default servlet container-based one. You can do that by configuring an instance of
DefaultWebSessionManager on Shiro's SecurityManager.
我们可以看到如果要用集群,就需要用本地会话,这里shiro给我准备了一个默认的native session manager,DefaultWebSessionManager,所以我们要修改spring配置文件,注入DefaultWebSessionManager
[html]
view plaincopy
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="sessionManager" ref="defaultWebSessionManager" />
<property name="realm" ref="shiroDbRealm" />
<property name="cacheManager" ref="memoryConstrainedCacheManager" />
</bean>
<bean id="defaultWebSessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="globalSessionTimeout" value="1200000" />
</bean>
[html]
view plain
copy
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="sessionManager" ref="defaultWebSessionManager" />
<property name="realm" ref="shiroDbRealm" />
<property name="cacheManager" ref="memoryConstrainedCacheManager" />
</bean>
<bean id="defaultWebSessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="globalSessionTimeout" value="1200000" />
</bean>
我们继续看DefaultWebSessionManager的源码,发现其父类DefaultSessionManager中有sessionDAO属性,这个属性是真正实现了session储存的类,这个就是我们自己实现的redis
session的储存类。
[java]
view plaincopy
protected SessionDAO sessionDAO;
private CacheManager cacheManager;
private boolean deleteInvalidSessions;
public DefaultSessionManager() {
this.deleteInvalidSessions = true;
this.sessionFactory = new SimpleSessionFactory();
this.sessionDAO = new MemorySessionDAO();
}
[java]
view plain
copy
protected SessionDAO sessionDAO;
private CacheManager cacheManager;
private boolean deleteInvalidSessions;
public DefaultSessionManager() {
this.deleteInvalidSessions = true;
this.sessionFactory = new SimpleSessionFactory();
this.sessionDAO = new MemorySessionDAO();
}
这里我们看到了,如果不自己注入sessionDAO,defaultWebSessionManager会使用MemorySessionDAO做为默认实现类,这个肯定不是我们想要的,所以这就自己动手实现sessionDAO吧。
[java]
view plaincopy
public class CustomShiroSessionDAO extends AbstractSessionDAO {
private ShiroSessionRepository shiroSessionRepository;
public ShiroSessionRepository getShiroSessionRepository() {
return shiroSessionRepository;
}
public void setShiroSessionRepository(
ShiroSessionRepository shiroSessionRepository) {
this.shiroSessionRepository = shiroSessionRepository;
}
@Override
public void update(Session session) throws UnknownSessionException {
getShiroSessionRepository().saveSession(session);
}
@Override
public void delete(Session session) {
if (session == null) {
LoggerUtil.error(CustomShiroSessionDAO.class,
"session can not be null,delete failed");
return;
}
Serializable id = session.getId();
if (id != null)
getShiroSessionRepository().deleteSession(id);
}
@Override
public Collection<Session> getActiveSessions() {
return getShiroSessionRepository().getAllSessions();
}
@Override
protected Serializable doCreate(Session session) {
Serializable sessionId = this.generateSessionId(session);
this.assignSessionId(session, sessionId);
getShiroSessionRepository().saveSession(session);
return sessionId;
}
@Override
protected Session doReadSession(Serializable sessionId) {
return getShiroSessionRepository().getSession(sessionId);
}
}
[java]
view plain
copy
public class CustomShiroSessionDAO extends AbstractSessionDAO {
private ShiroSessionRepository shiroSessionRepository;
public ShiroSessionRepository getShiroSessionRepository() {
return shiroSessionRepository;
}
public void setShiroSessionRepository(
ShiroSessionRepository shiroSessionRepository) {
this.shiroSessionRepository = shiroSessionRepository;
}
@Override
public void update(Session session) throws UnknownSessionException {
getShiroSessionRepository().saveSession(session);
}
@Override
public void delete(Session session) {
if (session == null) {
LoggerUtil.error(CustomShiroSessionDAO.class,
"session can not be null,delete failed");
return;
}
Serializable id = session.getId();
if (id != null)
getShiroSessionRepository().deleteSession(id);
}
@Override
public Collection<Session> getActiveSessions() {
return getShiroSessionRepository().getAllSessions();
}
@Override
protected Serializable doCreate(Session session) {
Serializable sessionId = this.generateSessionId(session);
this.assignSessionId(session, sessionId);
getShiroSessionRepository().saveSession(session);
return sessionId;
}
@Override
protected Session doReadSession(Serializable sessionId) {
return getShiroSessionRepository().getSession(sessionId);
}
}
我们自定义CustomShiroSessionDAO继承AbstractSessionDAO,实现对session操作的方法。这里为了便于扩展,我引入了一个接口ShiroSessionRepository,可以用redis、mongoDB等进行实现。
[java]
view plaincopy
public interface ShiroSessionRepository {
void saveSession(Session session);
void deleteSession(Serializable sessionId);
Session getSession(Serializable sessionId);
Collection<Session> getAllSessions();
}
[java]
view plain
copy
public interface ShiroSessionRepository {
void saveSession(Session session);
void deleteSession(Serializable sessionId);
Session getSession(Serializable sessionId);
Collection<Session> getAllSessions();
}
这个是我自己redis的ShiroSessionReposotory存储实现类:
[java]
view plaincopy
public class JedisShiroSessionRepository extends JedisManager implements
ShiroSessionRepository {
/**
* redis session key前缀
*/
private final String REDIS_SHIRO_SESSION = "shiro-session:";
@Autowired
private JedisPool jedisPool;
@Override
protected JedisPool getJedisPool() {
return jedisPool;
}
@Override
protected JedisDataType getJedisDataType() {
return JedisDataType.SESSION_CACHE;
}
@Override
public void saveSession(Session session) {
if (session == null || session.getId() == null) {
LoggerUtil.error(JedisShiroSessionRepository.class,
"session或者session id为空");
return;
}
byte[] key = SerializeUtil
.serialize(getRedisSessionKey(session.getId()));
byte[] value = SerializeUtil.serialize(session);
Jedis jedis = this.getJedis();
try {
Long timeOut = session.getTimeout() / 1000;
jedis.set(key, value);
jedis.expire(key, Integer.parseInt(timeOut.toString()));
} catch (JedisException e) {
LoggerUtil.error(JedisShiroSessionRepository.class, "保存session失败",
e);
} finally {
this.returnResource(jedis);
}
}
@Override
public void deleteSession(Serializable id) {
if (id == null) {
LoggerUtil.error(JedisShiroSessionRepository.class, "id为空");
return;
}
Jedis jedis = this.getJedis();
try {
jedis.del(SerializeUtil.serialize(getRedisSessionKey(id)));
} catch (JedisException e) {
LoggerUtil.error(JedisShiroSessionRepository.class, "删除session失败",
e);
} finally {
this.returnResource(jedis);
}
}
@Override
public Session getSession(Serializable id) {
if (id == null) {
LoggerUtil.error(JedisShiroSessionRepository.class, "id为空");
return null;
}
Session session = null;
Jedis jedis = this.getJedis();
try {
byte[] value = jedis.get(SerializeUtil
.serialize(getRedisSessionKey(id)));
session = SerializeUtil.deserialize(value, Session.class);
} catch (JedisException e) {
LoggerUtil.error(JedisShiroSessionRepository.class, "获取id为" + id
+ "的session失败", e);
} finally {
this.returnResource(jedis);
}
return session;
}
@Override
public Collection<Session> getAllSessions() {
Jedis jedis = this.getJedis();
Set<Session> sessions = new HashSet<Session>();
try {
Set<byte[]> byteKeys = jedis.keys(SerializeUtil
.serialize(this.REDIS_SHIRO_SESSION + "*"));
if (byteKeys != null && byteKeys.size() > 0) {
for (byte[] bs : byteKeys) {
Session s = SerializeUtil.deserialize(jedis.get(bs),
Session.class);
sessions.add(s);
}
}
} catch (JedisException e) {
LoggerUtil.error(JedisShiroSessionRepository.class,
"获取所有session失败", e);
} finally {
this.returnResource(jedis);
}
return sessions;
}
/**
* 获取redis中的session key
*
* @param sessionId
* @return
*/
private String getRedisSessionKey(Serializable sessionId) {
return this.REDIS_SHIRO_SESSION + sessionId;
}
}
[java]
view plain
copy
public class JedisShiroSessionRepository extends JedisManager implements
ShiroSessionRepository {
/**
* redis session key前缀
*/
private final String REDIS_SHIRO_SESSION = "shiro-session:";
@Autowired
private JedisPool jedisPool;
@Override
protected JedisPool getJedisPool() {
return jedisPool;
}
@Override
protected JedisDataType getJedisDataType() {
return JedisDataType.SESSION_CACHE;
}
@Override
public void saveSession(Session session) {
if (session == null || session.getId() == null) {
LoggerUtil.error(JedisShiroSessionRepository.class,
"session或者session id为空");
return;
}
byte[] key = SerializeUtil
.serialize(getRedisSessionKey(session.getId()));
byte[] value = SerializeUtil.serialize(session);
Jedis jedis = this.getJedis();
try {
Long timeOut = session.getTimeout() / 1000;
jedis.set(key, value);
jedis.expire(key, Integer.parseInt(timeOut.toString()));
} catch (JedisException e) {
LoggerUtil.error(JedisShiroSessionRepository.class, "保存session失败",
e);
} finally {
this.returnResource(jedis);
}
}
@Override
public void deleteSession(Serializable id) {
if (id == null) {
LoggerUtil.error(JedisShiroSessionRepository.class, "id为空");
return;
}
Jedis jedis = this.getJedis();
try {
jedis.del(SerializeUtil.serialize(getRedisSessionKey(id)));
} catch (JedisException e) {
LoggerUtil.error(JedisShiroSessionRepository.class, "删除session失败",
e);
} finally {
this.returnResource(jedis);
}
}
@Override
public Session getSession(Serializable id) {
if (id == null) {
LoggerUtil.error(JedisShiroSessionRepository.class, "id为空");
return null;
}
Session session = null;
Jedis jedis = this.getJedis();
try {
byte[] value = jedis.get(SerializeUtil
.serialize(getRedisSessionKey(id)));
session = SerializeUtil.deserialize(value, Session.class);
} catch (JedisException e) {
LoggerUtil.error(JedisShiroSessionRepository.class, "获取id为" + id
+ "的session失败", e);
} finally {
this.returnResource(jedis);
}
return session;
}
@Override
public Collection<Session> getAllSessions() {
Jedis jedis = this.getJedis();
Set<Session> sessions = new HashSet<Session>();
try {
Set<byte[]> byteKeys = jedis.keys(SerializeUtil
.serialize(this.REDIS_SHIRO_SESSION + "*"));
if (byteKeys != null && byteKeys.size() > 0) {
for (byte[] bs : byteKeys) {
Session s = SerializeUtil.deserialize(jedis.get(bs),
Session.class);
sessions.add(s);
}
}
} catch (JedisException e) {
LoggerUtil.error(JedisShiroSessionRepository.class,
"获取所有session失败", e);
} finally {
this.returnResource(jedis);
}
return sessions;
}
/**
* 获取redis中的session key
*
* @param sessionId
* @return
*/
private String getRedisSessionKey(Serializable sessionId) {
return this.REDIS_SHIRO_SESSION + sessionId;
}
}
这样sessionDAO我们就完成了,下面继续修改我们spring配置文件:
[html]
view plaincopy
<bean id="defaultWebSessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="globalSessionTimeout" value="1200000" />
<property name="sessionDAO" ref="customShiroSessionDAO" />
</bean>
<bean id="customShiroSessionDAO" class="com.xxx.security.shiro.custom.session.CustomShiroSessionDAO">
<property name="shiroSessionRepository" ref="jedisShiroSessionRepository" />
</bean>
<bean id="jedisShiroSessionRepository" class="com.xxx.security.shiro.custom.session.JedisShiroSessionRepository" />
[html]
view plain
copy
<bean id="defaultWebSessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="globalSessionTimeout" value="1200000" />
<property name="sessionDAO" ref="customShiroSessionDAO" />
</bean>
<bean id="customShiroSessionDAO" class="com.xxx.security.shiro.custom.session.CustomShiroSessionDAO">
<property name="shiroSessionRepository" ref="jedisShiroSessionRepository" />
</bean>
<bean id="jedisShiroSessionRepository" class="com.xxx.security.shiro.custom.session.JedisShiroSessionRepository" />
这样第一个问题,session的共享问题我们就解决好了,下一篇介绍另一个问题,cache的共享问题。
Apache Shiro集群要解决2个问题,一个是session的共享问题,一个是授权信息的cache共享问题,官网给的例子是Ehcache的实现,在配置说明上不算很详细,我这里用nosql(redis)替代了ehcache做了session和cache的存储。
shiro spring的默认配置(单机,非集群)
[html]
view plaincopy
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="shiroDbRealm" />
<property name="cacheManager" ref="memoryConstrainedCacheManager" />
</bean>
<!-- 自定义Realm -->
<bean id="shiroDbRealm" class="com.xxx.security.shiro.custom.ShiroDbRealm">
<property name="credentialsMatcher" ref="customCredentialsMather"></property>
</bean>
<!-- 用户授权信息Cache(本机内存实现) -->
<bean id="memoryConstrainedCacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login" />
<property name="successUrl" value="/project" />
<property name="filterChainDefinitions">
<value>
/login = authc
/logout = logout
</value>
</property>
</bean>
[html]
view plain
copy
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="shiroDbRealm" />
<property name="cacheManager" ref="memoryConstrainedCacheManager" />
</bean>
<!-- 自定义Realm -->
<bean id="shiroDbRealm" class="com.xxx.security.shiro.custom.ShiroDbRealm">
<property name="credentialsMatcher" ref="customCredentialsMather"></property>
</bean>
<!-- 用户授权信息Cache(本机内存实现) -->
<bean id="memoryConstrainedCacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login" />
<property name="successUrl" value="/project" />
<property name="filterChainDefinitions">
<value>
/login = authc
/logout = logout
</value>
</property>
</bean>
上面的配置是shiro非集群下的配置,DefaultWebSecurityManager类不需要注入sessionManager属性,它会使用默认的sessionManager类,请看源码
[java]
view plaincopy
public DefaultWebSecurityManager() {
super();
((DefaultSubjectDAO) this.subjectDAO).setSessionStorageEvaluator(new DefaultWebSessionStorageEvaluator());
this.sessionMode = HTTP_SESSION_MODE;
setSubjectFactory(new DefaultWebSubjectFactory());
setRememberMeManager(new CookieRememberMeManager());
setSessionManager(new ServletContainerSessionManager());
}
[java]
view plain
copy
public DefaultWebSecurityManager() {
super();
((DefaultSubjectDAO) this.subjectDAO).setSessionStorageEvaluator(new DefaultWebSessionStorageEvaluator());
this.sessionMode = HTTP_SESSION_MODE;
setSubjectFactory(new DefaultWebSubjectFactory());
setRememberMeManager(new CookieRememberMeManager());
setSessionManager(new ServletContainerSessionManager());
}
在最后一行,set了默认的servlet容器实现的sessionManager,sessionManager会管理session的创建、删除等等。如果我们需要让session在集群中共享,就需要替换这个默认的sessionManager。在其官网上原话是这样的:
[html]
view plaincopy
Native Sessions
If you want your session configuration settings and clustering to be portable across servlet containers
(e.g. Jetty in testing, but Tomcat or JBoss in production), or you want to control specific session/clustering
features, you can enable Shiro's native session management.
The word 'Native' here means that Shiro's own enterprise session management implementation will be used to support
all Subject and HttpServletRequest sessions and bypass the servlet container completely. But rest assured - Shiro
implements the relevant parts of the Servlet specification directly so any existing web/http related code works as
expected and never needs to 'know' that Shiro is transparently managing sessions.
DefaultWebSessionManager
To enable native session management for your web application, you will need to configure a native web-capable
session manager to override the default servlet container-based one. You can do that by configuring an instance of
DefaultWebSessionManager on Shiro's SecurityManager.
[html]
view plain
copy
Native Sessions
If you want your session configuration settings and clustering to be portable across servlet containers
(e.g. Jetty in testing, but Tomcat or JBoss in production), or you want to control specific session/clustering
features, you can enable Shiro's native session management.
The word 'Native' here means that Shiro's own enterprise session management implementation will be used to support
all Subject and HttpServletRequest sessions and bypass the servlet container completely. But rest assured - Shiro
implements the relevant parts of the Servlet specification directly so any existing web/http related code works as
expected and never needs to 'know' that Shiro is transparently managing sessions.
DefaultWebSessionManager
To enable native session management for your web application, you will need to configure a native web-capable
session manager to override the default servlet container-based one. You can do that by configuring an instance of
DefaultWebSessionManager on Shiro's SecurityManager.
我们可以看到如果要用集群,就需要用本地会话,这里shiro给我准备了一个默认的native session manager,DefaultWebSessionManager,所以我们要修改spring配置文件,注入DefaultWebSessionManager
[html]
view plaincopy
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="sessionManager" ref="defaultWebSessionManager" />
<property name="realm" ref="shiroDbRealm" />
<property name="cacheManager" ref="memoryConstrainedCacheManager" />
</bean>
<bean id="defaultWebSessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="globalSessionTimeout" value="1200000" />
</bean>
[html]
view plain
copy
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="sessionManager" ref="defaultWebSessionManager" />
<property name="realm" ref="shiroDbRealm" />
<property name="cacheManager" ref="memoryConstrainedCacheManager" />
</bean>
<bean id="defaultWebSessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="globalSessionTimeout" value="1200000" />
</bean>
我们继续看DefaultWebSessionManager的源码,发现其父类DefaultSessionManager中有sessionDAO属性,这个属性是真正实现了session储存的类,这个就是我们自己实现的redis
session的储存类。
[java]
view plaincopy
protected SessionDAO sessionDAO;
private CacheManager cacheManager;
private boolean deleteInvalidSessions;
public DefaultSessionManager() {
this.deleteInvalidSessions = true;
this.sessionFactory = new SimpleSessionFactory();
this.sessionDAO = new MemorySessionDAO();
}
[java]
view plain
copy
protected SessionDAO sessionDAO;
private CacheManager cacheManager;
private boolean deleteInvalidSessions;
public DefaultSessionManager() {
this.deleteInvalidSessions = true;
this.sessionFactory = new SimpleSessionFactory();
this.sessionDAO = new MemorySessionDAO();
}
这里我们看到了,如果不自己注入sessionDAO,defaultWebSessionManager会使用MemorySessionDAO做为默认实现类,这个肯定不是我们想要的,所以这就自己动手实现sessionDAO吧。
[java]
view plaincopy
public class CustomShiroSessionDAO extends AbstractSessionDAO {
private ShiroSessionRepository shiroSessionRepository;
public ShiroSessionRepository getShiroSessionRepository() {
return shiroSessionRepository;
}
public void setShiroSessionRepository(
ShiroSessionRepository shiroSessionRepository) {
this.shiroSessionRepository = shiroSessionRepository;
}
@Override
public void update(Session session) throws UnknownSessionException {
getShiroSessionRepository().saveSession(session);
}
@Override
public void delete(Session session) {
if (session == null) {
LoggerUtil.error(CustomShiroSessionDAO.class,
"session can not be null,delete failed");
return;
}
Serializable id = session.getId();
if (id != null)
getShiroSessionRepository().deleteSession(id);
}
@Override
public Collection<Session> getActiveSessions() {
return getShiroSessionRepository().getAllSessions();
}
@Override
protected Serializable doCreate(Session session) {
Serializable sessionId = this.generateSessionId(session);
this.assignSessionId(session, sessionId);
getShiroSessionRepository().saveSession(session);
return sessionId;
}
@Override
protected Session doReadSession(Serializable sessionId) {
return getShiroSessionRepository().getSession(sessionId);
}
}
[java]
view plain
copy
public class CustomShiroSessionDAO extends AbstractSessionDAO {
private ShiroSessionRepository shiroSessionRepository;
public ShiroSessionRepository getShiroSessionRepository() {
return shiroSessionRepository;
}
public void setShiroSessionRepository(
ShiroSessionRepository shiroSessionRepository) {
this.shiroSessionRepository = shiroSessionRepository;
}
@Override
public void update(Session session) throws UnknownSessionException {
getShiroSessionRepository().saveSession(session);
}
@Override
public void delete(Session session) {
if (session == null) {
LoggerUtil.error(CustomShiroSessionDAO.class,
"session can not be null,delete failed");
return;
}
Serializable id = session.getId();
if (id != null)
getShiroSessionRepository().deleteSession(id);
}
@Override
public Collection<Session> getActiveSessions() {
return getShiroSessionRepository().getAllSessions();
}
@Override
protected Serializable doCreate(Session session) {
Serializable sessionId = this.generateSessionId(session);
this.assignSessionId(session, sessionId);
getShiroSessionRepository().saveSession(session);
return sessionId;
}
@Override
protected Session doReadSession(Serializable sessionId) {
return getShiroSessionRepository().getSession(sessionId);
}
}
我们自定义CustomShiroSessionDAO继承AbstractSessionDAO,实现对session操作的方法。这里为了便于扩展,我引入了一个接口ShiroSessionRepository,可以用redis、mongoDB等进行实现。
[java]
view plaincopy
public interface ShiroSessionRepository {
void saveSession(Session session);
void deleteSession(Serializable sessionId);
Session getSession(Serializable sessionId);
Collection<Session> getAllSessions();
}
[java]
view plain
copy
public interface ShiroSessionRepository {
void saveSession(Session session);
void deleteSession(Serializable sessionId);
Session getSession(Serializable sessionId);
Collection<Session> getAllSessions();
}
这个是我自己redis的ShiroSessionReposotory存储实现类:
[java]
view plaincopy
public class JedisShiroSessionRepository extends JedisManager implements
ShiroSessionRepository {
/**
* redis session key前缀
*/
private final String REDIS_SHIRO_SESSION = "shiro-session:";
@Autowired
private JedisPool jedisPool;
@Override
protected JedisPool getJedisPool() {
return jedisPool;
}
@Override
protected JedisDataType getJedisDataType() {
return JedisDataType.SESSION_CACHE;
}
@Override
public void saveSession(Session session) {
if (session == null || session.getId() == null) {
LoggerUtil.error(JedisShiroSessionRepository.class,
"session或者session id为空");
return;
}
byte[] key = SerializeUtil
.serialize(getRedisSessionKey(session.getId()));
byte[] value = SerializeUtil.serialize(session);
Jedis jedis = this.getJedis();
try {
Long timeOut = session.getTimeout() / 1000;
jedis.set(key, value);
jedis.expire(key, Integer.parseInt(timeOut.toString()));
} catch (JedisException e) {
LoggerUtil.error(JedisShiroSessionRepository.class, "保存session失败",
e);
} finally {
this.returnResource(jedis);
}
}
@Override
public void deleteSession(Serializable id) {
if (id == null) {
LoggerUtil.error(JedisShiroSessionRepository.class, "id为空");
return;
}
Jedis jedis = this.getJedis();
try {
jedis.del(SerializeUtil.serialize(getRedisSessionKey(id)));
} catch (JedisException e) {
LoggerUtil.error(JedisShiroSessionRepository.class, "删除session失败",
e);
} finally {
this.returnResource(jedis);
}
}
@Override
public Session getSession(Serializable id) {
if (id == null) {
LoggerUtil.error(JedisShiroSessionRepository.class, "id为空");
return null;
}
Session session = null;
Jedis jedis = this.getJedis();
try {
byte[] value = jedis.get(SerializeUtil
.serialize(getRedisSessionKey(id)));
session = SerializeUtil.deserialize(value, Session.class);
} catch (JedisException e) {
LoggerUtil.error(JedisShiroSessionRepository.class, "获取id为" + id
+ "的session失败", e);
} finally {
this.returnResource(jedis);
}
return session;
}
@Override
public Collection<Session> getAllSessions() {
Jedis jedis = this.getJedis();
Set<Session> sessions = new HashSet<Session>();
try {
Set<byte[]> byteKeys = jedis.keys(SerializeUtil
.serialize(this.REDIS_SHIRO_SESSION + "*"));
if (byteKeys != null && byteKeys.size() > 0) {
for (byte[] bs : byteKeys) {
Session s = SerializeUtil.deserialize(jedis.get(bs),
Session.class);
sessions.add(s);
}
}
} catch (JedisException e) {
LoggerUtil.error(JedisShiroSessionRepository.class,
"获取所有session失败", e);
} finally {
this.returnResource(jedis);
}
return sessions;
}
/**
* 获取redis中的session key
*
* @param sessionId
* @return
*/
private String getRedisSessionKey(Serializable sessionId) {
return this.REDIS_SHIRO_SESSION + sessionId;
}
}
[java]
view plain
copy
public class JedisShiroSessionRepository extends JedisManager implements
ShiroSessionRepository {
/**
* redis session key前缀
*/
private final String REDIS_SHIRO_SESSION = "shiro-session:";
@Autowired
private JedisPool jedisPool;
@Override
protected JedisPool getJedisPool() {
return jedisPool;
}
@Override
protected JedisDataType getJedisDataType() {
return JedisDataType.SESSION_CACHE;
}
@Override
public void saveSession(Session session) {
if (session == null || session.getId() == null) {
LoggerUtil.error(JedisShiroSessionRepository.class,
"session或者session id为空");
return;
}
byte[] key = SerializeUtil
.serialize(getRedisSessionKey(session.getId()));
byte[] value = SerializeUtil.serialize(session);
Jedis jedis = this.getJedis();
try {
Long timeOut = session.getTimeout() / 1000;
jedis.set(key, value);
jedis.expire(key, Integer.parseInt(timeOut.toString()));
} catch (JedisException e) {
LoggerUtil.error(JedisShiroSessionRepository.class, "保存session失败",
e);
} finally {
this.returnResource(jedis);
}
}
@Override
public void deleteSession(Serializable id) {
if (id == null) {
LoggerUtil.error(JedisShiroSessionRepository.class, "id为空");
return;
}
Jedis jedis = this.getJedis();
try {
jedis.del(SerializeUtil.serialize(getRedisSessionKey(id)));
} catch (JedisException e) {
LoggerUtil.error(JedisShiroSessionRepository.class, "删除session失败",
e);
} finally {
this.returnResource(jedis);
}
}
@Override
public Session getSession(Serializable id) {
if (id == null) {
LoggerUtil.error(JedisShiroSessionRepository.class, "id为空");
return null;
}
Session session = null;
Jedis jedis = this.getJedis();
try {
byte[] value = jedis.get(SerializeUtil
.serialize(getRedisSessionKey(id)));
session = SerializeUtil.deserialize(value, Session.class);
} catch (JedisException e) {
LoggerUtil.error(JedisShiroSessionRepository.class, "获取id为" + id
+ "的session失败", e);
} finally {
this.returnResource(jedis);
}
return session;
}
@Override
public Collection<Session> getAllSessions() {
Jedis jedis = this.getJedis();
Set<Session> sessions = new HashSet<Session>();
try {
Set<byte[]> byteKeys = jedis.keys(SerializeUtil
.serialize(this.REDIS_SHIRO_SESSION + "*"));
if (byteKeys != null && byteKeys.size() > 0) {
for (byte[] bs : byteKeys) {
Session s = SerializeUtil.deserialize(jedis.get(bs),
Session.class);
sessions.add(s);
}
}
} catch (JedisException e) {
LoggerUtil.error(JedisShiroSessionRepository.class,
"获取所有session失败", e);
} finally {
this.returnResource(jedis);
}
return sessions;
}
/**
* 获取redis中的session key
*
* @param sessionId
* @return
*/
private String getRedisSessionKey(Serializable sessionId) {
return this.REDIS_SHIRO_SESSION + sessionId;
}
}
这样sessionDAO我们就完成了,下面继续修改我们spring配置文件:
[html]
view plaincopy
<bean id="defaultWebSessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="globalSessionTimeout" value="1200000" />
<property name="sessionDAO" ref="customShiroSessionDAO" />
</bean>
<bean id="customShiroSessionDAO" class="com.xxx.security.shiro.custom.session.CustomShiroSessionDAO">
<property name="shiroSessionRepository" ref="jedisShiroSessionRepository" />
</bean>
<bean id="jedisShiroSessionRepository" class="com.xxx.security.shiro.custom.session.JedisShiroSessionRepository" />
[html]
view plain
copy
<bean id="defaultWebSessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="globalSessionTimeout" value="1200000" />
<property name="sessionDAO" ref="customShiroSessionDAO" />
</bean>
<bean id="customShiroSessionDAO" class="com.xxx.security.shiro.custom.session.CustomShiroSessionDAO">
<property name="shiroSessionRepository" ref="jedisShiroSessionRepository" />
</bean>
<bean id="jedisShiroSessionRepository" class="com.xxx.security.shiro.custom.session.JedisShiroSessionRepository" />
这样第一个问题,session的共享问题我们就解决好了,下一篇介绍另一个问题,cache的共享问题。
相关文章推荐
- Apache Commons 常用工具类整理
- Apache benchmark
- Apache的prefork模式和worker模式
- apache下载安装配置
- Apache Spark Jobs 性能调优(一)
- Base64 Java org.apache.commons.codec.binary.Base64
- linux下装php5+mysql5+apache
- apache spark 结合 elasticsearch
- Kafka Manager - 一个管理 Apache Kafka 的工具
- ERROR org.apache.hadoop.yarn.server.resourcemanager.ResourceManager: RECEIVED SIGNAL 15: SIGTERM
- apache kafka监控系列-KafkaOffsetMonitor
- Apache服务器配置项和虚拟机配置
- Apache Lucene 5.x 集成中文分词库 IKAnalyzer
- 在本机部署Apache服务器
- 修改服务器Apache-Coyote/1.1标识为自定义内容
- 【手把手教你全文检索】Apache Lucene初探
- Apache 安装笔记
- Apache服务器 域名解析和绑定
- 本地下载的软件包制作成本地repo源并使用apache发布
- jmeter与apache测试网站并发