shiro+cas+spring-data-redis實現多系統單點登入和分散式專案的session同步
阿新 • • 發佈:2019-01-11
CSDN開通很久了,但是一直沒寫東西,2018年了,這是我CSDN的第一篇文章,歡迎各位評論探討和指點。
一、背景:
現在公司的業務系統要做多臺分散式叢集,由於是web專案,要做session同步,想到的方案是用目前火熱的redis資料庫儲存session,還有業務系統已經是使用shiro+cas做了單點登入的。
參考了一些行家的文章,自己加工寫了一個sharesession的專案,抽取成了一個jar包,可匯入需要同步session的業務系統。
二、專案簡介:
程式碼結構圖
使用gradle構建的專案
三、原始碼分析
1.gradle的構建檔案build.gradle如下
group 'com.gaojccn' version '1.0.0-SNAPSHOT' apply plugin: 'idea' apply plugin: 'java' apply plugin: "maven" apply plugin: 'groovy' sourceCompatibility = 1.7 compileJava.options.encoding = 'UTF-8' compileJava.options.compilerArgs = ["-Xlint:unchecked", "-Xlint:deprecation"] compileTestJava.options.encoding = 'UTF-8' compileTestJava.options.compilerArgs = ["-Xlint:unchecked", "-Xlint:deprecation"] buildscript { repositories { maven { url "https://plugins.gradle.org/m2/" } } } repositories { mavenCentral() /*本地中央倉庫*/ maven { url "http://192.168.35.26:8081/nexus/content/repositories/central/" } maven { url "http://192.168.35.26:8081/nexus/content/repositories/thirdparty/" } maven { url "http://192.168.35.26:8081/nexus/content/repositories/releases/" } maven { url "http://192.168.35.26:8081/nexus/content/repositories/snapshots/" } } dependencies { compile 'org.apache.shiro:shiro-core:1.2.4' compile 'org.apache.shiro:shiro-cas:1.2.4' compile 'org.apache.shiro:shiro-web:1.2.4' compile 'org.apache.shiro:shiro-all:1.2.4' compile 'org.apache.shiro:shiro-ehcache:1.2.4' compile("redis.clients:jedis:2.1.0") compile 'org.springframework.data:spring-data-redis:1.0.2.RELEASE' compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.4' //testCompile 'org.springframework:spring-test:3.1.2.RELEASE' //testCompile 'com.github.springtestdbunit:spring-test-dbunit:1.0.1' //testCompile 'org.unitils:unitils-dbunit:3.3' // testCompile group: 'junit', name: 'junit', version: '4.12' } project.ext { versionFile = file('version.properties') //版本屬性 } class ProjectVersion { Integer major Integer minor Integer bugfix Boolean release ProjectVersion(Integer major, Integer minor, Integer bugfix) { this.major = major this.minor = minor this.bugfix = bugfix this.release = Boolean.FALSE } ProjectVersion(Integer major, Integer minor, Integer bugfix, Boolean release) { this.major = major this.minor = minor this.bugfix = bugfix this.release = release } @Override String toString() { "$major.$minor.$bugfix${release ? '' : '-SNAPSHOT'}" } } task printVersion { doLast { logger.quiet("version:$version") } } task loadVersion { project.version = readVersion() } def isRelease() { ProjectVersion projectVersion = readVersion() projectVersion.release } ProjectVersion readVersion() { logger.quiet('Reading the version file.') if (!versionFile.exists()) { throw new GradleException("Required version file does not exists:$versionFile.canonicalPath") } Properties versionProps = new Properties() versionFile.withInputStream { stream -> versionProps.load(stream) } new ProjectVersion(versionProps.major.toInteger(), versionProps.minor.toInteger(), versionProps.bugfix.toInteger(), versionProps.release.toBoolean()) } task writeVersionFile << { def versionFilePath = 'version.json' File file = new File(versionFilePath) if (!file.exists()) file.createNewFile() def versionFile = new File(versionFilePath) versionFile.text = '{"version":"' + version + '"}' } uploadArchives { dependsOn build configuration = configurations.archives repositories.mavenDeployer { repository(url: 'http://192.168.35.26:8081/nexus/content/repositories/releases/') { authentication(userName: "admin", password: "admin123") } snapshotRepository(url: 'http://192.168.35.26:8081/nexus/content/repositories/snapshots/') { authentication(userName: "admin", password: "admin123") } pom.project { name 'gaojccn' packaging 'jar' description 'none' // url 'http://192.168.35.26:8081/nexus/content/repositories/releases/' url 'http://192.168.35.26:8081/nexus/content/repositories/snapshots/' groupId "com.gaojccn" artifactId "sharesession" version version } } } processResources { dependsOn writeVersionFile } jar { baseName = 'sharesession' version version manifest { attributes 'Implementation-Title': 'session', 'Implementation-Version': version, 'Created-By': 'gaojc' } } task makeReleaseVersion(group: 'versioning', description: 'Makes project a release version.') << { ant.propertyfile(file: versionFile) { entry(key: 'release', type: 'string', operation: '=', value: 'true') } }
2.主配置檔案share_session.xml
<?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:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd"> <!--redis連線配置--> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxIdle" value="${redis.maxIdle}"/> <property name="maxActive" value="${redis.maxActive}"/> <property name="maxWait" value="${redis.maxWait}"/> <property name="testOnBorrow" value="${redis.testOnBorrow}"/> </bean> <bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <property name="hostName" value="${redis.host}"/> <property name="port" value="${redis.port}"/> <property name="database" value="${redis.database}"/> <property name="timeout" value="${redis.timeout}"/> <property name="poolConfig" ref="poolConfig"/> </bean> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="redisConnectionFactory"/> </bean> <!--shiro配置--> <!-- Shiro生命週期處理器--> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> <!-- SecurityManager --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!-- session管理 --> <property name="sessionManager" ref="sessionManager"/> <property name="subjectFactory" ref="casSubjectFactory"/> <property name="realms"> <list> <ref bean="casRealm"/> </list> </property> </bean> <!-- session管理 --> <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> <!-- 會話超時時間,單位:毫秒 --> <property name="deleteInvalidSessions" value="true"></property> <property name="sessionDAO" ref="sessionDao"></property> <property name="cacheManager" ref="cacheManager" /> <!-- sessionIdCookie的實現,用於重寫覆蓋容器預設的JSESSIONID --> <property name="sessionIdCookie" ref="sharesession"/> <!-- 定時清理失效會話, 清理使用者直接關閉瀏覽器造成的孤立會話 --> <property name="sessionValidationInterval" value="${session.sessionValidationInterval}"/> <!--session事件監聽器--> <property name="sessionListeners"> <list> <ref bean="redisSessionListener"/> </list> </property> </bean> <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager" /> <!-- sessionIdCookie的實現,用於重寫覆蓋容器預設的JSESSIONID --> <bean id="sharesession" class="org.apache.shiro.web.servlet.SimpleCookie"> <!-- cookie的name,對應的預設是 JSESSIONID --> <constructor-arg value="${session.cookieName}"/> <!-- jsessionId的path為 / 用於多個系統共享jsessionId --> <property name="path" value="/"/> <property name="httpOnly" value="true"/> <!--maxAge=-1表示瀏覽器關閉時失效此Cookie--> <property name="maxAge" value="-1"/> </bean> <!--casSubjectFactory--> <bean id="casSubjectFactory" class="org.apache.shiro.cas.CasSubjectFactory"/> <!--casRealm--> <bean id="casRealm" class="org.apache.shiro.cas.CasRealm"> <property name="defaultRoles" value="${casRealm.roles}"/> <property name="casServerUrlPrefix" value="${casRealm.casServerUrlPrefix}"/> <property name="casService" value="${casRealm.casService}"/> </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> <!-- 安全認證過濾器 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <property name="loginUrl" value="${loginUrl}"/> <property name="successUrl" value="/index.html" /> <property name="unauthorizedUrl" value="/error.jsp"/> <property name="filters"> <util:map> <entry key="casFilter" value-ref="casFilter"/> <entry key="authc" value-ref="authc"/> <entry key="roles" value-ref="roles"/> <entry key="rest" value-ref="rest"/> <entry key="logout" value-ref="logout"/> </util:map> </property> <property name="filterChainDefinitions" value="${filterChainDefinitions}"/> </bean> <!--CAS單點登入filter--> <bean id="casFilter" class="org.apache.shiro.cas.CasFilter"> <property name="failureUrl" value="${casFilter.failureUrl}"/> </bean> <!--authc filter--> <bean id="authc" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter"/> <!--roles filter--> <bean id="roles" class="org.apache.shiro.web.filter.authz.RolesAuthorizationFilter"/> <!--rest filter--> <bean id="rest" class="org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter"> <property name="loginUrl" value="${loginUrl}"/> </bean> <!--logout filter--> <bean id="logout" class="org.apache.shiro.web.filter.authc.LogoutFilter"> <property name="redirectUrl" value="${logout.redirectUrl}"/> </bean> </beans>
<!--redis連線配置--> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxIdle" value="${redis.maxIdle}"/> <property name="maxActive" value="${redis.maxActive}"/> <property name="maxWait" value="${redis.maxWait}"/> <property name="testOnBorrow" value="${redis.testOnBorrow}"/> </bean> <bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <property name="hostName" value="${redis.host}"/> <property name="port" value="${redis.port}"/> <property name="database" value="${redis.database}"/> <property name="timeout" value="${redis.timeout}"/> <property name="poolConfig" ref="poolConfig"/> </bean> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="redisConnectionFactory"/> </bean> <!--shiro配置--> <!-- Shiro生命週期處理器--> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> <!-- SecurityManager --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!-- session管理 --> <property name="sessionManager" ref="sessionManager"/> <property name="subjectFactory" ref="casSubjectFactory"/> <property name="realms"> <list> <ref bean="casRealm"/> </list> </property> </bean> <!-- session管理 --> <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> <!-- 會話超時時間,單位:毫秒 --> <property name="deleteInvalidSessions" value="true"></property> <property name="sessionDAO" ref="sessionDao"></property> <property name="cacheManager" ref="cacheManager" /> <!-- sessionIdCookie的實現,用於重寫覆蓋容器預設的JSESSIONID --> <property name="sessionIdCookie" ref="sharesession"/> <!-- 定時清理失效會話, 清理使用者直接關閉瀏覽器造成的孤立會話 --> <property name="sessionValidationInterval" value="${session.sessionValidationInterval}"/> <!--session事件監聽器--> <property name="sessionListeners"> <list> <ref bean="redisSessionListener"/> </list> </property> </bean> <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager" /> <!-- sessionIdCookie的實現,用於重寫覆蓋容器預設的JSESSIONID --> <bean id="sharesession" class="org.apache.shiro.web.servlet.SimpleCookie"> <!-- cookie的name,對應的預設是 JSESSIONID --> <constructor-arg value="${session.cookieName}"/> <!-- jsessionId的path為 / 用於多個系統共享jsessionId --> <property name="path" value="/"/> <property name="httpOnly" value="true"/> <!--maxAge=-1表示瀏覽器關閉時失效此Cookie--> <property name="maxAge" value="-1"/> </bean> <!--casSubjectFactory--> <bean id="casSubjectFactory" class="org.apache.shiro.cas.CasSubjectFactory"/> <!--casRealm--> <bean id="casRealm" class="org.apache.shiro.cas.CasRealm"> <property name="defaultRoles" value="${casRealm.roles}"/> <property name="casServerUrlPrefix" value="${casRealm.casServerUrlPrefix}"/> <property name="casService" value="${casRealm.casService}"/> </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> <!-- 安全認證過濾器 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <property name="loginUrl" value="${loginUrl}"/> <property name="successUrl" value="/index.html" /> <property name="unauthorizedUrl" value="/error.jsp"/> <property name="filters"> <util:map> <entry key="casFilter" value-ref="casFilter"/> <entry key="authc" value-ref="authc"/> <entry key="roles" value-ref="roles"/> <entry key="rest" value-ref="rest"/> <entry key="logout" value-ref="logout"/> </util:map> </property> <property name="filterChainDefinitions" value="${filterChainDefinitions}"/> </bean> <!--CAS單點登入filter--> <bean id="casFilter" class="org.apache.shiro.cas.CasFilter"> <property name="failureUrl" value="${casFilter.failureUrl}"/> </bean> <!--authc filter--> <bean id="authc" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter"/> <!--roles filter--> <bean id="roles" class="org.apache.shiro.web.filter.authz.RolesAuthorizationFilter"/> <!--rest filter--> <bean id="rest" class="org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter"> <property name="loginUrl" value="${loginUrl}"/> </bean> <!--logout filter--> <bean id="logout" class="org.apache.shiro.web.filter.authc.LogoutFilter"> <property name="redirectUrl" value="${logout.redirectUrl}"/> </bean> </beans>
3.src目錄下的java檔案
3.1 RedisManager
package com.gaojccn.sharesession;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.dao.DataAccessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Service;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
@Service
public class RedisManager {
private static Logger logger = LoggerFactory.getLogger(RedisManager.class);
@Autowired
private RedisTemplate<String, Serializable> redisTemplate;
private RedisSerializer<String> serializer = new StringRedisSerializer();
/**
* 新增快取資料(給定key已存在,進行覆蓋)
*
* @param key
* @param obj
* @throws DataAccessException
*/
public <T> void set(String key, T obj) throws DataAccessException {
final byte[] bkey = serializer.serialize(key);
final byte[] bvalue = serializer.serialize(obj.toString());
logger.info("set key {} value {}", key, obj);
redisTemplate.execute(new RedisCallback<Void>() {
@Override
public Void doInRedis(RedisConnection connection) throws DataAccessException {
connection.set(bkey, bvalue);
return null;
}
});
}
/**
* 新增快取資料(給定key已存在,不進行覆蓋,直接返回false)
*
* @param key
* @param obj
* @return 操作成功返回true,否則返回false
* @throws DataAccessException
*/
public <T> boolean setNX(String key, T obj) throws DataAccessException {
final byte[] bkey = serializer.serialize(key);
final byte[] bvalue = serializer.serialize(obj.toString());
logger.info("setNX key {} value {}", key, obj);
boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
return connection.setNX(bkey, bvalue);
}
});
return result;
}
/**
* 新增快取資料,設定快取失效時間
*
* @param key
* @param obj
* @param expireSeconds 過期時間,單位 秒
* @throws DataAccessException
*/
public <T> void setEx(String key, T obj, final long expireSeconds) throws DataAccessException {
final byte[] bkey = serializer.serialize(key);
final byte[] bvalue = serializer.serialize(obj.toString());
logger.info("setEx key {} value {}", key, obj);
redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
connection.setEx(bkey, expireSeconds/1000, bvalue);
return true;
}
});
}
/**
* 獲取key對應value
*
* @param key
* @return
* @throws DataAccessException
*/
public <T> T get(final String key) throws DataAccessException {
final byte[] keyStr = serializer.serialize(key);
return get(keyStr);
}
/**
* 根據 key位元組陣列 獲取value
*
* @param keyStr
* @param <T>
* @return
*/
public <T> T get(final byte[] keyStr) {
T result = redisTemplate.execute(new RedisCallback<T>() {
public T doInRedis(RedisConnection connection)
throws DataAccessException {
byte[] value = connection.get(keyStr);
return deseriaValueByte(value);
}
});
return result;
}
/**
* 反序列化value位元組陣列
*
* @param value
* @param <T>
* @return
*/
private <T> T deseriaValueByte(byte[] value) {
if (value == null) {
return null;
}
String valueStr = serializer.deserialize(value);
T retStr;
try {
retStr = SerializableUtils.deserialize(valueStr);
} catch (Exception e) {
logger.error("deseriaValueByte {} happen RuntimeException {}", value, e.getMessage());
return null;
}
return retStr;
}
/**
* 刪除指定key資料
*
* @param key
* @return 返回操作影響記錄數
*/
public Long delete(final String key) throws DataAccessException {
logger.info("delete key {} from redis", key);
if (StringUtils.isEmpty(key)) {
return 0l;
}
Long delNum = redisTemplate.execute(new RedisCallback<Long>() {
@Override
public Long doInRedis(RedisConnection connection) throws DataAccessException {
byte[] keys = serializer.serialize(key);
return connection.del(keys);
}
});
return delNum;
}
/**
* 根據key模糊查詢value set集合
*
* @param key
* @param <T>
* @return
* @throws DataAccessException
*/
public <T> Set<T> keys(final String key) throws DataAccessException {
if (StringUtils.isEmpty(key)) {
return null;
}
Set<T> res = redisTemplate.execute(new RedisCallback<Set<T>>() {
public Set<T> doInRedis(RedisConnection connection)
throws DataAccessException {
Set<T> tSet = new HashSet<>();
byte[] keys = serializer.serialize(key);
Set<byte[]> keysByteSet = connection.keys(keys);
if (keysByteSet != null && keysByteSet.size() > 0)
for (byte[] key : keysByteSet) {
byte[] valueByte = connection.get(key);
T value = deseriaValueByte(valueByte);
tSet.add(value);
}
return tSet;
}
});
return res;
}
/**
* 清空快取
*
* @return
*/
public boolean flushDB() throws DataAccessException {
logger.info("flushDB in redis...");
boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
public Boolean doInRedis(RedisConnection connection)
throws DataAccessException {
connection.flushDb();
return true;
}
});
return result;
}
}
3.2 RedisSessionDao
package com.gaojccn.sharesession;
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;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.Serializable;
import java.util.Collection;
import java.util.Set;
/**
* RedisSessionDao
*/
@Service("sessionDao")
public class RedisSessionDao extends AbstractSessionDAO {
private static Logger logger = LoggerFactory.getLogger(RedisSessionDao.class);
@Autowired
private RedisManager redisManager;
//設定過期時間
@Value("${session.expireTime}")
private long expireTime;
// The Redis key prefix for the sessions
@Value("${session.keyPrefix}")
private String keyPrefix;
@Override
public Serializable doCreate(Session session) {
Serializable sessionId = this.generateSessionId(session);
this.assignSessionId(session, formatSessionId(sessionId));
logger.info("session id is" + session.getId());
this.saveSession(session);
return session.getId();
}
private String formatSessionId(Serializable sid) {
try {
String sessionId = String.valueOf(sid).replace("-", "").toUpperCase();
return sessionId;
} catch (Exception e) {
logger.error("formatSessionId happen exception {}", e.getMessage());
return null;
}
}
@Override
public Session doReadSession(Serializable sessionId) {
if (sessionId == null) {
logger.error("session id is null");
return null;
}
Session s = redisManager.get(keyPrefix + sessionId);
return s;
}
@Override
public void update(Session session) throws UnknownSessionException {
this.saveSession(session);
}
private void saveSession(Session session) {
if (session == null || session.getId() == null) {
logger.error("session or session id is null");
return;
}
session.setTimeout(expireTime);
redisManager.setEx(keyPrefix + session.getId(), SerializableUtils.serialize(session), expireTime);
}
@Override
public void delete(Session session) {
if (session == null || session.getId() == null) {
logger.error("session or session id is null");
return;
}
redisManager.delete(keyPrefix + session.getId());
}
@Override
public Collection<Session> getActiveSessions() {
Set<Session> sessions = redisManager.keys(this.keyPrefix + "*");
return sessions;
}
}
3.3 RedisSessionListener
package com.gaojccn.sharesession;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.SessionListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* redisSession事件監聽器
* author:gaojc
*/
@Service
public class RedisSessionListener implements SessionListener {
private static final Logger logger = LoggerFactory.getLogger(RedisSessionListener.class);
@Autowired
private RedisSessionDao sessionDao;
@Override
public void onStart(Session session) {//會話建立時觸發
logger.debug("會話建立:" + session.getId());
}
@Override
public void onExpiration(Session session) {//會話過期時觸發
logger.debug("會話過期:" + session.getId());
sessionDao.delete(session);
}
@Override
public void onStop(Session session) {//退出時觸發
logger.info("會話停止:" + session.getId());
sessionDao.delete(session);
}
}
3.4 SerializableUtils
package com.gaojccn.sharesession;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.session.Session;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SerializableUtils {
public static String serialize(Session session) {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(session);
return Base64.encodeToString(bos.toByteArray());
} catch (Exception e) {
throw new RuntimeException("serialize session error", e);
}
}
public static <T> T deserialize(String sessionStr) {
try {
ByteArrayInputStream bis = new ByteArrayInputStream(Base64.decode(sessionStr));
ObjectInputStream ois = new ObjectInputStream(bis);
return (T) ois.readObject();
} catch (Exception e) {
throw new RuntimeException("deserialize session error", e);
}
}
}
4. web專案中的使用
4.1 匯入sharesession.jar
4.2 web.xml裡面加入shiro過濾器
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
而且用contextLoaderListener載入spring配置檔案
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-context.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
4.3 spring-context.xml裡面加入相關檔案
<!--共享session properties配置-->
<context:property-placeholder location="classpath:/properties/share_session.properties"
file-encoding="UTF-8" ignore-unresolvable="true"/>
<!--共享session bean配置-->
<import resource="classpath*:share_session.xml"/>
4.4 share_session.properties
##redis連線引數
redis.host=192.168.1.109
#這裡用的是簡單的單節點
redis.port=6379
redis.database=0
redis.maxIdle=300
redis.maxActive=600
redis.maxWait=1000
redis.testOnBorrow=true
#當客戶端閒置多長時間後關閉連線,如果指定為0,表示關閉該功能
redis.timeout=10000
#session有效時間30分鐘 單位毫秒
session.expireTime=1800000
#sessionId字首
session.keyPrefix=redis_session:
#redis session alias (session cookieName)(session key名稱,用和web容器預設一樣的名稱jsessionid防止出錯)
session.cookieName=jsessionid
#定時清理失效會話間隔時間 20分鐘
session.sessionValidationInterval=1200000
#shiro url配置
loginUrl=https://cas-ad.share.gaojccn.com:8443/cas/login?service=http://netpay-web.gaojccn.com:8080/netpay/shiro-cas
casFilter.failureUrl=/error.jsp
logout.redirectUrl=https://cas-ad.share.gaojccn.com:8443/cas/logout
#shiroFilter.webDir=netpayweb
#casRealm
casRealm.roles=ROLE_USER
casRealm.casServerUrlPrefix=https://cas-ad.share.gaojccn.com:8443/cas
casRealm.casService=http://netpay-web.gaojccn.com:8080/netpay/shiro-cas
#filterChainDefinitions 用\n換行來分割多行value
filterChainDefinitions=/shiro-cas = cas\n/rest/version = anon\n/rest/** = authc\n/netpayweb/version.json = anon\n/netpayweb/report/** = anon\n/netpayweb/** = authc\n/logout = logout
ok,到此結束,測試使用吧。