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 ...
随机推荐
- Confluence 6 使用 JConsole 监控远程 Confluence
针对生产系统中,我们推荐你使用远程监控,这个将不会消耗你远程 Confluence 服务器的资源. 启动远程监控: 添加下面的属性到 setenv.sh / setenv.bat 文件中,端口你可以定 ...
- LeetCode(92):反转链表 II
Medium! 题目描述: 反转从位置 m 到 n 的链表.请使用一趟扫描完成反转. 说明:1 ≤ m ≤ n ≤ 链表长度. 示例: 输入: 1->2->3->4->5-&g ...
- Python的图像库
对数字图像基本的处理的学习按照下面两个博客: Python的图像库(Opencv.PIL.matplotlib.skimage)的使用(读取.存储.变换.滤波) python数字图像处理
- linux下的抓包
1. 查看网卡名字 cat /proc/net/dev 2.抓取外网进来的包 tcpdump -i eth0 port -s -w .pcap 3.抓取自己服务器上的两个程序之间访问的数据 换成 lo ...
- 集腋成裘-05-angularJS -初识angular
私以为angular的最大特点是:只关注数据 1.1 angular之:双向绑定 <!DOCTYPE html> <html ng-app=""> < ...
- Python迷宫游戏(基础版)
# 画地图map_data = [ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 1, 1, 1, 1, 1, 1, 1, 1], [1, 2, 1, 0, 0, 0, ...
- Oracle数据重复,只取一条
--方法一 select * from tb_supply where rowid=any(select max(rowid) from tb_supply group by phone_id) -- ...
- golang 请求带验证信息的坑
最近用golang 和python对接接口,由于之前验证那块没有设置好,然后又为了进度,最近决定用http自带的basic 验证, php的代码很快就验证通过了 /** * @param $url * ...
- HTML文本格式化与HTML 超链接
文本格式化<b>加粗文本</b><i>斜体文本</i><code>电脑自动输出</code><sub> 下标< ...
- 【BZOJ4155】[Ipsc2015]Humble Captains
题解: 第一问裸的最小割 第二问考虑贪心 我们把边权平均分配给两个点 然后就变成了给n个数分两组差最小 np-hard问题 暴力背包,操作存在区间左移,右移,or bieset优化