使用Redis实现Session共享

发表时间:2018-03-20 10:50:46 浏览量( 23 ) 留言数( 0 )

学习目标:

1、Shiro使用Redis作为缓存

2、Shiro的使用Redis共享Session


学习过程:

   defaultWebSessionManager会使用MemorySessionDAO做为默认实现类,这个肯定不是我们想要的,所以这就自己动手实现sessionDAO吧。Session共享在分布式运行环境中是必须要解决得问题,这里我们使用Shiro提供Session管理接口,把Session会话保存在Redis中。。上一节课我们使用的是Ehcache作为本地的缓存的。这节课我们使用redis缓存数据,所以下面这个配置就可以先注释了,不过这里我们先实现自定义得CacheManager

一、实现自己得CacheManager

<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
		<property name="cacheManagerConfigFile" value="classpath:ehcache-shiro.xml" />
	</bean>

   1、实现Cache操作类

public class RedisCache<K, V> implements Cache<K,V>,Serializable {
    private final String REDIS_SHIRO_CACHE = "shiro-cache:";
    private String cacheKey;
    private RedisTemplate<K, V> redisTemplate;
    private long globExpire = 300;  //这个需要改成通过配置文件配置的。
    
    public RedisCache(RedisTemplate redisTemplate){
        this.cacheKey=this.REDIS_SHIRO_CACHE+":";
        this.redisTemplate = redisTemplate;
    }

    public RedisCache(String name, RedisTemplate redisTemplate,long globExpire){
        this.cacheKey=this.REDIS_SHIRO_CACHE+name+":";
        this.redisTemplate = redisTemplate;
        this.globExpire=globExpire;
    }
    @Override
    public V get(K key) throws CacheException {
        redisTemplate.boundValueOps(getCacheKey(key)).expire(globExpire,TimeUnit.SECONDS);
        return redisTemplate.boundValueOps(getCacheKey(key)).get();
    }

    @Override
    public V put(K key, V value) throws CacheException {
        V old = get(key);
        redisTemplate.boundValueOps(getCacheKey(key)).set(value);
        return old;
    }

    @Override
    public V remove(K key) throws CacheException {
        V old = get(key);
        redisTemplate.delete(getCacheKey(key));
        return old;
    }

    @Override
    public void clear() throws CacheException {
        redisTemplate.delete(keys());
    }

    @Override
    public int size() {
        return keys().size();
    }

    @Override
    public Set keys() {
        return redisTemplate.keys(getCacheKey("*"));
    }

    @Override
    public Collection values() {
        Set<K> set = keys();
        List list = new  LinkedList();
        for(K s :set){
            list.add(get(s));
        }
        return list;
    }

    private K getCacheKey(Object k){
        return (K) (this.cacheKey+k);
    }

}

2、实现CacheManager

public class RedisCacheManager implements CacheManager {

	private RedisTemplate<String, Object> redisTemplate;
	
	private static final Logger logger = LoggerFactory.getLogger(RedisCacheManager.class);

	// fast lookup by name map
	private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>();

	public <K, V> Cache<K, V> getCache(String name) throws CacheException {
		logger.debug("获取名称为: " + name + " 的RedisCache实例");
		Cache c = caches.get(name);
		if (c == null) {
			c = new RedisCache<K, V>(redisTemplate);
			caches.put(name, c);
		}
		return c;
	}

	public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
		this.redisTemplate = redisTemplate;
	}
	
	
}

配置文件配置该CacheManager

     <bean id="cacheManager" class="com.shiro.myfilter.RedisCacheManager">
        <property name="redisTemplate" ref="redisTemplate"></property>
     </bean>



二、Session共享

1、实现自己的SessionDao

public class RedisSessionDao extends EnterpriseCacheSessionDAO {
	
	 @Resource
	 private RedisTemplate<String, Object> redisTemplate;

	 private int expireTime=600;
	 

	// 创建session,保存到数据库
	@Override
	protected Serializable doCreate(Session session) {
		Serializable sessionId = super.doCreate(session);
        System.out.println("创建session,sessionId:"+sessionId);
		redisTemplate.opsForValue().set(sessionId.toString(), session);
		redisTemplate.expire(sessionId.toString(),expireTime, TimeUnit.SECONDS);
		return sessionId;
	}

	// 获取session
	@Override
	protected Session doReadSession(Serializable sessionId) {
		// 先从缓存中获取session,如果没有再去数据库中获取
		Session session = super.doReadSession(sessionId);
		 System.out.println("获取session,sessionId:"+sessionId+",session="+session);
		if (session == null) {
			
			session = (Session) redisTemplate.opsForValue().get(sessionId.toString());
		}
		return session;
	}

	// 更新session的最后一次访问时间
	@Override
	protected void doUpdate(Session session) {
		super.doUpdate(session);
		String sessionId =  session.getId().toString();
		System.out.println("更新session,sessionId:"+sessionId);
		if (!redisTemplate.hasKey(sessionId)) {
			redisTemplate.opsForValue().set(sessionId, session);
		}
		redisTemplate.expire(sessionId, expireTime, TimeUnit.SECONDS);
	}

	// 删除session
	@Override
	protected void doDelete(Session session) {
		super.doDelete(session);
		String sessionId =  session.getId().toString();
		System.out.println("删除session,sessionId:"+sessionId);
		redisTemplate.delete( sessionId);
	}
}

2、Spring的配置

    <bean id="cacheManager" class="com.shiro.myfilter.RedisCacheManager">
        <property name="redisTemplate" ref="redisTemplate"></property>
     </bean>

     <bean id="redisSessionDao" class="com.shiro.myfilter.RedisSessionDao">
	   <property name="cacheManager" ref="cacheManager"></property>
	</bean>

	<!-- 安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="myCustomRealm" />
		<property name="cacheManager" ref="cacheManager" />
		<property name="sessionManager" ref="sessionManager" />
		<!-- 记住我 -->
		<property name="rememberMeManager" ref="rememberMeManager" />
	</bean>


我们可以这个项目的代码在复制一份项目出来,修改端口,使用nginx配置负载均衡,看一下Session有没有共享

upstream TestUp{

        server localhost:9090;

        server localhost:9091;

    }



   location /spring {

            proxy_set_header        Host $host:$proxy_port;


            proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;


            proxy_set_header        X-Real-IP $remote_addr;


            proxy_pass http://TestUp/spring/;


            proxy_redirect default;

        }


启动nginx,redis和两个项目看一下是否能共享