Redis+Shiro+Spring-data-redis,共享Session
环境:centos7,Java1.8+,一个Nginx,两个Tomcat,一个Redis。
关于共享session的问题大家都应该知道了,传统的部署项目,两个相同的项目部署到不同的服务器上,Nginx负载均衡后会导致用户在A上登陆了,经过负载均衡后,在B上要重新登录,因为A上有相关session信息,而B没有。这种情况也称为“有状态”服务。而“无状态”服务则是:在一个公共的地方存储session,每次访问都会统一到这个地方来拿。

为了方便我只用了一台linux虚拟机。
如果你使用了Shiro权限管理呢,他的好处之一就是会话管理,由Shiro管理Session,而不是Tomcat。听说都在用Spring-Session,实际上它和shiro一样,请看web.xml里面的配置
<!-- spring-session Filter
<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
--> <!-- Shiro Filter is defined in the spring application context: -->
<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>
不难看出,这两个都是基于:org.springframework.web.filter.DelegatingFilterProxy
下面开始正题。
使用Shiro需要继承AbstractSessionDAO
package com.internetsaying.auth.session; import java.io.Serializable;
import java.util.Collection;
import java.util.List; import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import com.internetsaying.auth.redis.RedisManager; @Component
public class MySessionDao extends AbstractSessionDAO { @Autowired
private RedisManager redisManager; @Override
public void delete(Session session) {
if(session == null || session.getId() == null){
System.out.println("Session is null");
return;
}
redisManager.hdelete(session.getId().toString());
} @Override
public Collection<Session> getActiveSessions() {
List<Session> list = redisManager.hmget();
return list;
} @Override
public void update(Session session) throws UnknownSessionException {
if(session == null || session.getId() == null){
System.out.println("Session is null");
return;
}
Serializable sessionId = session.getId();
redisManager.hadd(sessionId.toString(), session);
} @Override
protected Serializable doCreate(Session session) {
Serializable sessionId = generateSessionId(session);
assignSessionId(session, sessionId);
//添加进redis
redisManager.hadd(sessionId.toString(), session); return sessionId;
} @Override
protected Session doReadSession(Serializable sessionId) {
return redisManager.hget(sessionId.toString());
} }
下面是RedisManager的代码
package com.internetsaying.auth.redis; import java.util.ArrayList;
import java.util.List; import org.apache.shiro.session.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component; @Component
public class RedisManager { @Autowired
private RedisTemplate<String, Session> redisTemplate; private static final String KEY = "shareSessionMap"; public void hadd(String sessionId, byte[] bytes){
redisTemplate.boundHashOps(KEY).put(sessionId, bytes);
}
public void hadd(String sessionId, Session session){
redisTemplate.boundHashOps(KEY).put(sessionId, session);
} public void hdelete(String sessionId){
redisTemplate.boundHashOps(KEY).delete(sessionId);
} public Session hget(String sessionId){
return (Session) redisTemplate.boundHashOps(KEY).get(sessionId);
} public List<Session> hmget(){
List<Session> list = new ArrayList<>(); List<Object> values = redisTemplate.boundHashOps(KEY).values();
for (Object object : values) {
list.add((Session) object);
}
return list;
}
}
配置文件:shiro和redis部分(只是与共享session相关的代码)
<?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:context="http://www.springframework.org/schema/context"
xmlns:core="http://activemq.apache.org/schema/core"
xmlns:redis="http://www.springframework.org/schema/redis"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core
http://www.springframework.org/schema/redis http://www.springframework.org/schema/redis/spring-redis-1.0.xsd"> <!-- SecurityManager -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- session管理 -->
<property name="sessionManager" ref="sessionManager"></property>
</bean>
<!-- redis操作 -->
<bean id="redisManager" class="com.internetsaying.auth.redis.RedisManager"></bean>
<!-- Session ID 生成器 -->
<bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"></bean>
<!-- SessionDao实现类 -->
<bean id="sessionDAO" class="com.internetsaying.auth.session.MySessionDao">
<property name="sessionIdGenerator" ref="sessionIdGenerator"></property>
</bean>
<!-- session管理 -->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="globalSessionTimeout" value="1800000"></property>
<property name="deleteInvalidSessions" value="true"></property>
<property name="sessionDAO" ref="sessionDAO"></property>
<!-- sessionIdCookie的实现,用于重写覆盖容器默认的JSESSIONID -->
<property name="sessionIdCookie" ref="sharesession" />
</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> <!-- 以下是Redis -->
<context:property-placeholder location="classpath:redis.properties"/>
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}"></property>
<property name="maxTotal" value="${redis.maxTotal}"></property>
<property name="maxWaitMillis" value="${redis.maxWaitMillis}"></property>
<property name="testOnBorrow" value="${redis.testOnBorrow}"></property>
</bean> <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="usePool" value="${redis.pooled}"></property>
<property name="hostName" value="${redis.host}"></property>
<property name="port" value="${redis.port}"></property>
<property name="poolConfig" ref="jedisPoolConfig"></property>
</bean>
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory"></property>
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"></bean>
</property>
<!-- 解决读取int类型value值报错的问题 -->
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"></bean>
</property>
<property name="hashKeySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"></bean>
</property>
</bean> </beans>
配置文件:redis.properties
#Redis settings redis.host=127.0.0.1
redis.port=6379
#redis.pass=
redis.maxIdle=30
redis.maxTotal=100
redis.maxWaitMillis=1000
redis.testOnBorrow=true
redis.pooled=true
执行流程

说明:由于做的是分布式项目,不便贴上全部代码,如遇问题可联系我:3110320051.
我有个完整的小栗子
部署
关于Nginx做负载均衡可参考我上一篇文章。
这里将项目打包后,分别上传到两个Tomcat,启动。当访问时。【页面过于简单,仅供测试】

点击登录既完成Shiro的认证授权,跳转到可访问页面。

刷新

注意cookie的Value
现在我们看一下redis存储的:
[root@localhost local]# ./bin/redis-cli
127.0.0.1:6379> keys *
1) "shareSessionMap"
127.0.0.1:6379> HKEYS shareSessionMap
1) "a511c5c4-9f61-4c24-aad2-b551f8053ebf"
所以:每次服务器都是去redis里面去拿的session,登录A后,切换到B不需要重新登录。
Redis+Shiro+Spring-data-redis,共享Session的更多相关文章
- Redis与Spring Data Redis
1.Redis概述 1.1介绍 官网:https://redis.io/ Redis是一个开源的使用ANSIC语言编写.支持网络.可基于内存 亦可持久化的日志型.Key-Value型的高性能数据库. ...
- Redis(八):spring data redis 理解
前言 Spring Data Redis project,应用了Spring概念来开发使用键值形式的数据存储的解决方案.我们(官方)提供了一个 "template" ,这是一个高级 ...
- Redis客户端 Spring Data Redis(未完)
官网:http://projects.spring.io/spring-data-redis/ 1.0 参考之前的一片文章:Gradle入门实战(Windows版) 构建java applicati ...
- Spring Data Redis整体介绍 (一)
为什么使用Spring Data Redis 首先Spring Data Redis 是Spring 框架提供的用于操作Redis的客户端. Spring框架是一个全栈Java程序框架,通过DI.AO ...
- spring boot通过Spring Data Redis集成redis
在spring boot中,默认集成的redis是Spring Data Redis,Spring Data Redis针对redis提供了非常方便的操作模版RedisTemplate idea中新建 ...
- Spring Data Redis 让 NoSQL 快如闪电(2)
[编者按]本文作者为 Xinyu Liu,文章的第一部分重点概述了 Redis 方方面面的特性.在第二部分,将介绍详细的用例.文章系国内 ITOM 管理平台 OneAPM 编译呈现. 把 Redis ...
- Spring Data Redis学习
本文是从为知笔记上复制过来的,懒得调整格式了,为知笔记版本是带格式的,内容也比这里全.点这里 为知笔记版本 Spring Data Redis 学习 Version 1.8.4.Release 前言 ...
- Spring Boot使用Spring Data Redis操作Redis(单机/集群)
说明:Spring Boot简化了Spring Data Redis的引入,只要引入spring-boot-starter-data-redis之后会自动下载相应的Spring Data Redis和 ...
- 使用Spring Data Redis操作Redis(集群版)
说明:请注意Spring Data Redis的版本以及Spring的版本!最新版本的Spring Data Redis已经去除Jedis的依赖包,需要自行引入,这个是个坑点.并且会与一些低版本的Sp ...
- 使用Spring Data Redis操作Redis(单机版)
说明:请注意Spring Data Redis的版本以及Spring的版本!最新版本的Spring Data Redis已经去除Jedis的依赖包,需要自行引入,这个是个坑点.并且会与一些低版本的Sp ...
随机推荐
- 【翻译】关于vertical-align所有你需要知道的
本文是翻译过来的,如果有不对的地方还请指教~,原文链接:Vertical-Align: All You Need To Know 前面一些说明,可以略过不看吧 我经常需要对元素进行垂直方向上的布局. ...
- LeetCode(83): 删除排序链表中的重复元素
Easy! 题目描述: 给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次. 示例 1: 输入: 1->1->2 输出: 1->2 示例 2: 输入: 1->1-&g ...
- ubuntu MySQL的安装
https://i.cnblogs.com/EditPosts.aspx?opt=1 https://juejin.im/entry/5adb5deff265da0b9d77cb3b MySQL Co ...
- arm指令1
.section .text.writeFUNCTION(write) ldr r12, =__NR_write swi #0 bx lr LDR: LDR 的两种用法 1)LDR pc, =MyHa ...
- time与datetime模块
在python中,通常用下面几种方式来表示时间: 时间戳(timestamp):通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量. 格式化的时间字符串(format s ...
- Play框架--初学笔记
目录结构 web_app 根目录 | sbt SBT Unix 批处理脚本用于启动sbt-launch.jar | sbt.bat SBT Windows 批处理脚本用于启动sbt-launch.ja ...
- GnuPGP介绍
PGP(Pretty Good Privacy的首字母):PGP公司的加密.签名工具套件,使用了商业版本的IDEA算法,并集成了有商业版权的PGPdisk工具. GnuPG(GNU Privacy G ...
- sublime快捷键的使用
Sublime text 3是码农最喜欢的代码编辑器,每天和代码打交道,必先利其器,掌握基本的代码编辑器的快捷键,能让你打码更有效率.刚开始可能有些生疏,只要花一两个星期坚持使用并熟悉这些常用的快捷键 ...
- java.lang.NoClassDefFoundError: javax/servlet/AsyncListener解决方案
问题:spring3.2的架构在tomcat6.0中无法正常启动,抛出java.lang.NoClassDefFoundError: javax/servlet/AsyncListener错误 原因: ...
- 如何设置Navicat的显示字体与字体大小?
方法/步骤 打开Navicat 点击[工具]菜单,再选择[选项] 在[选项]界面,点击[外观]下的[字体] 设置网格字体和大小 设置编辑器字体和大小 设置命令列界面字体和大 ...