Apache shiro集群实现 (一) shiro入门介绍

Apache shiro集群实现 (二) shiro 的INI配置

Apache shiro集群实现 (三)shiro身份认证(Shiro Authentication)

Apache shiro集群实现 (四)shiro授权(Authentication)--访问控制

Apache shiro集群实现 (五)分布式集群系统下的高可用session解决方案

Apache shiro集群实现 (六)分布式集群系统下的高可用session解决方案---Session共享

Apache Shiro的基本配置和构成这里就不详细说明了,其官网有说明文档,这里仅仅说明集群的解决方案,详细配置:shiro web config

Apache Shiro集群要解决2个问题,一个是session的共享问题,一个是授权信息的cache共享问题,官网给的例子是Ehcache的实现,在配置说明上不算很详细,我这里用nosql(redis)替代了ehcache做了session和cache的存储。

shiro spring的默认配置(单机,非集群)

<span style="font-size:18px;"><span style="font-size:18px;"><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> </span></span>

上面的配置是shiro非集群下的配置,DefaultWebSecurityManager类不需要注入sessionManager属性,它会使用默认的sessionManager类,请看源码

<span style="font-size:18px;"><span style="font-size:18px;">    public DefaultWebSecurityManager() {
super();
((DefaultSubjectDAO) this.subjectDAO).setSessionStorageEvaluator(new DefaultWebSessionStorageEvaluator());
this.sessionMode = HTTP_SESSION_MODE;
setSubjectFactory(new DefaultWebSubjectFactory());
setRememberMeManager(new CookieRememberMeManager());
setSessionManager(new ServletContainerSessionManager());
} </span></span>

在最后一行,set了默认的servlet容器实现的sessionManager,sessionManager会管理session的创建、删除等等。如果我们需要让session在集群中共享,就需要替换这个默认的sessionManager。在其官网上原话是这样的:

<span style="font-size:18px;"><span style="font-size:18px;">    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. </span></span>

我们可以看到如果要用集群,就需要用本地会话,这里shiro给我准备了一个默认的native session manager,DefaultWebSessionManager,所以我们要修改spring配置文件,注入DefaultWebSessionManager

<span style="font-size:18px;"><span style="font-size:18px;">    <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> </span></span>

我们继续看DefaultWebSessionManager的源码,发现其父类DefaultSessionManager中有sessionDAO属性,这个属性是真正实现了session储存的类,这个就是我们自己实现的redis session的储存类。

<span style="font-size:18px;"><span style="font-size:18px;">    protected SessionDAO sessionDAO;  

    private CacheManager cacheManager;  

    private boolean deleteInvalidSessions;  

    public DefaultSessionManager() {
this.deleteInvalidSessions = true;
this.sessionFactory = new SimpleSessionFactory();
this.sessionDAO = new MemorySessionDAO();
} </span></span>

这里我们看到了,如果不自己注入sessionDAO,defaultWebSessionManager会使用MemorySessionDAO做为默认实现类,这个肯定不是我们想要的,所以这就自己动手实现sessionDAO吧。

<span style="font-size:18px;"><span style="font-size:18px;">package com.tgb.itoo.authority.cache;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set; import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class RedisSessionDAO extends AbstractSessionDAO { private static Logger logger = LoggerFactory.getLogger(RedisSessionDAO.class);
/**
* shiro-redis的session对象前缀
*/
private RedisManager redisManager; /**
* The Redis key prefix for the sessions
*/
private String keyPrefix = "shiro_redis_session:"; @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");
return;
} byte[] key = getByteKey(session.getId());
byte[] value = SerializeUtils.serialize(session);
session.setTimeout(redisManager.getExpire()*1000);
this.redisManager.set(key, value, redisManager.getExpire());
} @Override
public void delete(Session session) {
if(session == null || session.getId() == null){
logger.error("session or session id is null");
return;
}
redisManager.del(this.getByteKey(session.getId())); } //用来统计当前活动的session
@Override
public Collection<Session> getActiveSessions() {
Set<Session> sessions = new HashSet<Session>(); Set<byte[]> keys = redisManager.keys(this.keyPrefix + "*");
if(keys != null && keys.size()>0){
for(byte[] key:keys){
Session s = (Session)SerializeUtils.deserialize(redisManager.get(key));
sessions.add(s);
}
} return sessions;
} @Override
protected Serializable doCreate(Session session) {
Serializable sessionId = this.generateSessionId(session);
this.assignSessionId(session, sessionId);
this.saveSession(session);
return sessionId;
} @Override
protected Session doReadSession(Serializable sessionId) {
if(sessionId == null){
logger.error("session id is null");
return null;
} Session s = (Session)SerializeUtils.deserialize(redisManager.get(this.getByteKey(sessionId)));
return s;
} /**
* 获得byte[]型的key
* @param key
* @return
*/
private byte[] getByteKey(Serializable sessionId){
String preKey = this.keyPrefix + sessionId;
return preKey.getBytes();
} public RedisManager getRedisManager() {
return redisManager;
} public void setRedisManager(RedisManager redisManager) {
this.redisManager = redisManager; /**
* 初始化redisManager
*/
this.redisManager.init();
} /**
* Returns the Redis session keys
* prefix.
* @return The prefix
*/
public String getKeyPrefix() {
return keyPrefix;
} /**
* Sets the Redis sessions key
* prefix.
* @param keyPrefix The prefix
*/
public void setKeyPrefix(String keyPrefix) {
this.keyPrefix = keyPrefix;
}
}
</span></span>

我们自定义RedisSessionDAO继承AbstractSessionDAO,实现对session操作的方法,可以用redis、mongoDB等进行实现。

这个是自己redis的RedisCacheManager存储实现类:

<span style="font-size:18px;"><span style="font-size:18px;">package com.tgb.itoo.authority.cache;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class RedisCacheManager implements CacheManager{ private static final Logger logger = LoggerFactory
.getLogger(RedisCacheManager.class); // fast lookup by name map
private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>(); private RedisManager redisManager; /**
* The Redis key prefix for caches
*/
private String keyPrefix = "shiro_redis_cache:"; /**
* Returns the Redis session keys
* prefix.
* @return The prefix
*/
public String getKeyPrefix() {
return keyPrefix;
} /**
* Sets the Redis sessions key
* prefix.
* @param keyPrefix The prefix
*/
public void setKeyPrefix(String keyPrefix) {
this.keyPrefix = keyPrefix;
} @Override
public <K, V> Cache<K, V> getCache(String name) throws CacheException {
logger.debug("获取名称为: " + name + " 的RedisCache实例"); Cache c = caches.get(name); if (c == null) { // initialize the Redis manager instance
redisManager.init(); // create a new cache instance
c = new RedisCache<K, V>(redisManager, keyPrefix); // add it to the cache collection
caches.put(name, c);
}
return c;
} public RedisManager getRedisManager() {
return redisManager;
} public void setRedisManager(RedisManager redisManager) {
this.redisManager = redisManager;
}
}
</span></span>

这样RedisSessionDAO我们就完成了,下面继续修改我们spring配置文件:

<span style="font-size:18px;"><?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee"
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.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd"
default-lazy-init="true"> <!-- 注解支持 -->
<context:annotation-config />
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:config/shiro-cas.properties</value>
</list>
</property>
</bean> <jee:local-slsb id="PermissionManager"
jndi-name="java:global/itoo-authority-role-ear/itoo-authority-shiro-core-0.0.1-SNAPSHOT/PermissionManager!com.tgb.itoo.authority.service.ShiroBean"
business-interface="com.tgb.itoo.authority.service.ShiroBean"></jee:local-slsb> <!-- <jee:jndi-lookup id="PermissionManager" --> <!-- jndi-name="ejb:itoo-authority-shiro-ear/itoo-authority-shiro-core-0.0.1-SNAPSHOT/PermissionManager!com.tgb.itoo.authority.service.ShiroBean" --> <!-- lookup-on-startup="true" cache="true" environment-ref="evn"> --> <!-- </jee:jndi-lookup> -->
<!-- shiro过滤器 start -->
<bean id="shiroSecurityFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"></property>
<property name="loginUrl" value="${loginUrl}"></property>
<property name="successUrl" value="${successUrl}"></property>
<property name="filters">
<map>
<entry key="casFilter">
<bean class="org.apache.shiro.cas.CasFilter">
<!--配置验证错误时的失败页面 /main 为系统登录页面 -->
<property name="failureUrl" value="/message.jsp" />
</bean>
</entry>
</map>
</property>
<!-- 过滤器链,请求url对应的过滤器 -->
<property name="filterChainDefinitions">
<value>
/message.jsp=anon
/logout=logout
/shiro-cas=casFilter
/** =user
</value>
</property>
</bean>
<!-- shiro过滤器 end --> <!-- 保证实现shiro内部的生命周期函数bean的执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> <!-- 第三:shiro管理中心类 start-李社河 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="shiroRealm"></property>
<property name="sessionMode" value="http"></property>
<property name="subjectFactory" ref="casSubjectFactory"></property>
<!-- ehcahe缓存shiro自带 -->
<!-- <property name="cacheManager" ref="shiroEhcacheManager"></property> --> <!-- redis缓存 -->
<property name="cacheManager" ref="redisCacheManager" /> <!-- sessionManager -->
<property name="sessionManager" ref="sessionManager"></property>
</bean> <!-- 缓存管理器redis-start-李社河-2015年4月14日 --> <!-- sessionManager -->
<!-- <bean id="sessionManager" -->
<!-- class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> -->
<!-- <property name="sessionDAO" ref="redisSessionDAO" /> -->
<!-- </bean> --> <!-- 自定义redisManager-redis -->
<bean id="redisCacheManager" class="com.tgb.itoo.authority.cache.RedisCacheManager">
<property name="redisManager" ref="redisManager" />
</bean>
<!-- 自定义cacheManager -->
<bean id="redisCache" class="com.tgb.itoo.authority.cache.RedisCache">
<constructor-arg ref="redisManager"></constructor-arg>
</bean> <bean id="redisManager" class="com.tgb.itoo.authority.cache.RedisManager"></bean> <!-- 缓存管理器redis-end-李社河-2015年4月14日 --> <!-- session会话存储的实现类 -->
<bean id="redisShiroSessionDAO" class="com.tgb.itoo.authority.cache.RedisSessionDAO">
<property name="redisManager" ref="redisManager" />
</bean> <!-- sessionManager -->
<!-- session管理器 -->
<bean id="sessionManager"
class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<!-- 设置全局会话超时时间,默认30分钟(1800000) -->
<property name="globalSessionTimeout" value="1800000" />
<!-- 是否在会话过期后会调用SessionDAO的delete方法删除会话 默认true -->
<property name="deleteInvalidSessions" value="true" /> <!-- 会话验证器调度时间 -->
<property name="sessionValidationInterval" value="1800000" /> <!-- session存储的实现 -->
<property name="sessionDAO" ref="redisShiroSessionDAO" />
<!-- sessionIdCookie的实现,用于重写覆盖容器默认的JSESSIONID -->
<property name="sessionIdCookie" ref="sharesession" />
<!-- 定时检查失效的session -->
<property name="sessionValidationSchedulerEnabled" value="true" /> </bean> <!-- sessionIdCookie的实现,用于重写覆盖容器默认的JSESSIONID -->
<bean id="sharesession" class="org.apache.shiro.web.servlet.SimpleCookie">
<!-- cookie的name,对应的默认是 JSESSIONID -->
<constructor-arg name="name" value="SHAREJSESSIONID" />
<!-- jsessionId的path为 / 用于多个系统共享jsessionId -->
<property name="path" value="/" />
<property name="httpOnly" value="true"/>
</bean> <!-- 第一:shiro于数据交互的类 ,自己写的类的实现-ShiroRealmBean自己重写的类的实现 -->
<bean id="shiroRealm" class="com.tgb.itoo.authority.service.ShiroRealmBean">
<property name="defaultRoles" value="user"></property> <!-- 注入自己实现的类,授权的过程-PermissionManager是云平台重写的授权的过程,用户Id->角色->资源->查找权限-李社河2015年4月15日 -->
<property name="permissionMgr" ref="PermissionManager"></property>
<property name="casServerUrlPrefix" value="${casServerUrlPrefix}"></property>
<property name="casService" value="${casService}"></property>
</bean> <bean id="casSubjectFactory" class="org.apache.shiro.cas.CasSubjectFactory" /> <!-- shiro的自带缓存管理器encahe -->
<!-- <bean id="shiroEhcacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> -->
<!-- <property name="cacheManagerConfigFile" value="classpath:config/ehcache-shiro.xml"
/> -->
<!-- </bean> --> <!-- 开启shiro的注解,需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证 -->
<bean
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor"></bean>
<bean
class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
</beans>
</span>

这样第一个问题,session的共享问题我们就解决好了,下一篇介绍另一个问题,cache的共享问题。

Apache shiro集群实现 (六)分布式集群系统下的高可用session解决方案---Session共享的更多相关文章

  1. Apache shiro集群实现 (五)分布式集群系统下的高可用session解决方案

    Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...

  2. 分布式集群系统下的高可用session解决方案

    目前,为了使web能适应大规模的访问,需要实现应用的集群部署. 而实现集群部署首先要解决session的统一,即需要实现session的共享机制. 目前,在集群系统下实现session统一的有如下几种 ...

  3. 浅谈web应用的负载均衡、集群、高可用(HA)解决方案(转)

    1.熟悉几个组件 1.1.apache     —— 它是Apache软件基金会的一个开放源代码的跨平台的网页服务器,属于老牌的web服务器了,支持基于Ip或者域名的虚拟主机,支持代理服务器,支持安 ...

  4. 浅谈web应用的负载均衡、集群、高可用(HA)解决方案

    http://aokunsang.iteye.com/blog/2053719   声明:以下仅为个人的一些总结和随写,如有不对之处,还请看到的网友指出,以免误导. (详细的配置方案请google,这 ...

  5. web应用的负载均衡、集群、高可用(HA)解决方案

    看看别人的文章: 1.熟悉几个组件 1.1.apache     —— 它是Apache软件基金会的一个开放源代码的跨平台的网页服务器,属于老牌的web服务器了,支持基于Ip或者域名的虚拟主机,支持代 ...

  6. CYQ.Data 支持分布式数据库(主从备)高可用及负载调试

    前言: 继上一篇,介绍 CYQ.Data 在分布式缓存上支持高可用,详见:CYQ.Data 对于分布式缓存Redis.MemCache高可用的改进及性能测试 本篇介绍 CYQ.Data 在对数据库层面 ...

  7. solr 集群(SolrCloud 分布式集群部署步骤)

    SolrCloud 分布式集群部署步骤 安装软件包准备 apache-tomcat-7.0.54 jdk1.7 solr-4.8.1 zookeeper-3.4.5 注:以上软件都是基于 Linux ...

  8. Redis集群模式之分布式集群模式

    前言 Redis集群模式主要有2种: 主从集群 分布式集群. 前者主要是为了高可用或是读写分离,后者为了更好的存储数据,负载均衡. 本文主要讲解主从集群.本章主要讲解后一半部分,Redis集群. 与本 ...

  9. CentOS 7下搭建高可用集群

    一 .安装集群软件 必须软件pcs,pacemaker,corosync,fence-agents-all,如果需要配置相关服务,也要安装对应的软件. 二.配置防火墙1.禁止防火墙和selinux# ...

随机推荐

  1. 使用 dotnet cli 命令上传 nuget 程序包

    前言 前面写了一篇文章介绍了如何将自己的程序集打包成nuget package并上传到nuget.org,传送门.全部是通过网页端来进行操作的,现在介绍一种比较方便快捷的方法就是用dotnet cli ...

  2. 【Swift】ios开发中巧用 description 打印对象时,打印对象的属性

    ios开发中我们打印对象的时候,会直接输出对象地址,这样不方便我们开发.我们可以 巧用 description 打印对象时,输出对象的属性 在oc中直接重写即可.swift中需要遵守Printable ...

  3. [NOIp 2012]同余方程

    Description 求关于 x 的同余方程 ax ≡ 1 (mod b)的最小正整数解. Input 输入只有一行,包含两个正整数 a, b,用一个空格隔开. Output 输出只有一行,包含一个 ...

  4. [SDOI2016]排列计数

    Description 求有多少种长度为 n 的序列 A,满足以下条件: 1 ~ n 这 n 个数在序列中各出现了一次 若第 i 个数 A[i] 的值为 i,则称 i 是稳定的.序列恰好有 m 个数是 ...

  5. bzoj 5251: [2018多省省队联测]劈配

    Description 一年一度的综艺节目<中国新代码>又开始了. Zayid从小就梦想成为一名程序员,他觉得这是一个展示自己的舞台,于是他毫不犹豫地报名了. 题目描述 轻车熟路的Zayi ...

  6. bzoj 3277: 串

    Description 字符串是oi界常考的问题.现在给定你n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中 至少k个字符串的子串(注意包括本身). Solution 出现 \(k ...

  7. ●BZOJ 1272 [BeiJingWc2008]Gate Of Babylon

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=1272 题解: 容斥,Lucas定理本题的容斥考虑类似 [BZOJ 1042 [HAOI200 ...

  8. bzoj3825 NOI2017 游戏

    题目背景 狂野飙车是小 L 最喜欢的游戏.与其他业余玩家不同的是,小 L 在玩游戏之余,还精于研究游戏的设计,因此他有着与众不同的游戏策略. 题目描述 小 L 计划进行nn 场游戏,每场游戏使用一张地 ...

  9. NOIP2016 玩脱记

    NOIP前: NOIP前停课了一个多月,这一个多月里浪得飞起,内心十分紧张,然后就不知不觉就到NOIP了. Day 0: 上火车前ryc给我们出了道题"一个数列,只有两个数出现了奇数次,找出 ...

  10. css修改浏览器默认的滚动条样式

    //滚动条样式 ::-webkit-scrollbar { width: 10px; } /* 垂直滚动条的滑动块 */ ::-webkit-scrollbar-thumb:vertical { bo ...