spring-shiro 整合請看shiro springmvc整合第一步
spring-redis整合請看spring redis整合
用到的redis jar如下
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.6.2</version></dependency><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-redis</artifactId><version>1.4.1.RELEASE</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.2</version></dependency>
想要實現使用redis管理session 需要在shiro 的sessionmanager添加sessionDAO屬性 如下
<bean id="sessionManager"class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"><property name="sessionDAO" ref="sessionDao"></property><property name="globalSessionTimeout" value="60000" /><!-- 刪除失效session --><property name="sessionValidationSchedulerEnabled" value="true" /><property name="sessionListeners" ref="myShiroSessionListener"></property></bean><bean id="myShiroSessionListener" class="com.zyc.listener.MyShiroSessionListener"></bean><bean id="sessionDao" class="com.zyc.security.SessionDao"><property name="redisUtil" ref="redisUtil"></property></bean>
redis配置如下
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"><property name="maxIdle" value="100" /><property name="maxWaitMillis" value="10000" /><property name="testOnBorrow" value="true" /></bean><!-- redis伺服器中心 --><bean id="connectionFactory"class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"><property name="poolConfig" ref="poolConfig" /><property name="port" value="63791" /><property name="hostName" value="127.0.0.1" /><!-- <property name="password" value="${redis.password}" /> --><property name="timeout" value="10000"></property></bean><bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"><property name="connectionFactory" ref="connectionFactory" /><property name="keySerializer"><beanclass="org.springframework.data.redis.serializer.StringRedisSerializer" /></property><property name="valueSerializer"><beanclass="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" /></property></bean>
sessionDao需要實現EnterpriseCacheSessionDAO類或者CachingSessionDAO類,咱們這裡以EnterpriseCacheSessionDAO類為例如下:
package com.zyc.security;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutput;import java.io.ObjectOutputStream;import java.io.Serializable;import org.apache.shiro.session.Session;import org.apache.shiro.session.mgt.SimpleSession;import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;public class SessionDao extends EnterpriseCacheSessionDAO {private RedisUtil redisUtil;public RedisUtil getRedisUtil() {return redisUtil;}public void setRedisUtil(RedisUtil redisUtil) {this.redisUtil = redisUtil;}// 建立session,儲存到資料庫 @Override protected Serializable doCreate(Session session) { Serializable sessionId = super.doCreate(session); redisUtil.set(sessionId.toString(), sessionToByte(session),1*60L); return sessionId; } // 擷取session @Override protected Session doReadSession(Serializable sessionId) { // 先從緩衝中擷取session,如果沒有再去資料庫中擷取 Session session = super.doReadSession(sessionId); if(session == null){ byte[] bytes = (byte[]) redisUtil.get(sessionId.toString()); if(bytes != null && bytes.length > 0){ session = byteToSession(bytes); } } return session; } // 更新session的最後一次訪問時間 @Override protected void doUpdate(Session session) { super.doUpdate(session); redisUtil.set(session.getId().toString(), sessionToByte(session),1*60L); } // 刪除session @Override protected void doDelete(Session session) { super.doDelete(session); redisUtil.remove(session.getId().toString()); } // 把session對象轉化為byte儲存到redis中 public byte[] sessionToByte(Session session){ ByteArrayOutputStream bo = new ByteArrayOutputStream(); byte[] bytes = null; try { ObjectOutput oo = new ObjectOutputStream(bo); oo.writeObject(session); bytes = bo.toByteArray(); } catch (IOException e) { e.printStackTrace(); } return bytes; } // 把byte還原為session public Session byteToSession(byte[] bytes){ ByteArrayInputStream bi = new ByteArrayInputStream(bytes); ObjectInputStream in; SimpleSession session = null; try { in = new ObjectInputStream(bi); session = (SimpleSession) in.readObject(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return session; }}RedisUtil工具類如下:
package com.zyc.security;import java.io.Serializable;import java.util.Set;import java.util.concurrent.TimeUnit;import javax.annotation.Resource;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.ValueOperations;import org.springframework.stereotype.Service;/** * @author: zyc * @date:2017年10月24日 上午9:26:55 * @description: * @version : * */@Service("redisUtil")public class RedisUtil {private Logger logger = LoggerFactory.getLogger(RedisUtil.class);@Resource(name = "redisTemplate")private RedisTemplate<Serializable, Object> redisTemplate;/** * 大量刪除對應的value * * @param keys */public void remove(final String... keys) {for (String key : keys) {remove(key);}}/** * 大量刪除key * * @param pattern */public void removePattern(final String pattern) {Set<Serializable> keys = redisTemplate.keys(pattern);if (keys.size() > 0)redisTemplate.delete(keys);}/** * 刪除對應的value * * @param key */public void remove(final String key) {if (exists(key)) {redisTemplate.delete(key);}}/** * 判斷緩衝中是否有對應的value * * @param key * @return */public boolean exists(final String key) {return redisTemplate.hasKey(key);}/** * 擷取所有的key * * @return */public Set<Serializable> keys() {return redisTemplate.keys("*");}/** * 讀取緩衝 * * @param key * @return */public Object get(final String key) {Object result = null;ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();result = operations.get(key);return result;}/** * 寫入緩衝 * * @param key * @param value * @return */public boolean set(final String key, Object value) {boolean result = false;try {ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();operations.set(key, value);result = true;} catch (Exception e) {e.printStackTrace();}return result;}/** * 寫入緩衝 * * @param key * @param value * @return */public boolean set(final String key, Object value, Long expireTime) {boolean result = false;try {ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();operations.set(key, value);redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);result = true;} catch (Exception e) {e.printStackTrace();}return result;}public void setRedisTemplate(RedisTemplate<Serializable, Object> redisTemplate) {this.redisTemplate = redisTemplate;}}
session監聽類如下(可寫可不寫,不寫需要在配置中將監聽器給去掉):
package com.zyc.listener;import org.apache.shiro.session.Session;import org.apache.shiro.session.SessionListener;import com.zyc.security.RedisUtil;import com.zyc.security.SpringContext;public class MyShiroSessionListener implements SessionListener {@Overridepublic void onStart(Session session) {}@Overridepublic void onStop(Session session) {// TODO Auto-generated method stubSystem.out.println("onStop==="+session.getId());RedisUtil redisUtil=(RedisUtil) SpringContext.getBean("redisUtil");redisUtil.remove(session.getId().toString());}@Overridepublic void onExpiration(Session session) {System.out.println("onExpiration==="+session.getId());RedisUtil redisUtil=(RedisUtil) SpringContext.getBean("redisUtil");redisUtil.remove(session.getId().toString());}}SpringContext是擷取bean的一個工具類:
package com.zyc.security;import org.springframework.beans.BeansException;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.stereotype.Component;@Componentpublic class SpringContext implements ApplicationContextAware{private static ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext arg0)throws BeansException {// TODO Auto-generated method stubapplicationContext=arg0;}public static Object getBean(String name){if(applicationContext!=null){return applicationContext.getBean(name);}return null;}}