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 ...
随机推荐
- better-scroll项目中遇到的问题
1.在项目中发现个问题,用better-scroll实现的轮播图和页面滚动条俩个效果一起出现的时候,当鼠标或手指放在轮播图位置的时候,上下滚动的时候,页面滚动条不动 发现最新的版本就会出这个问题,就是 ...
- js的页面传值cookie.session
jquery的cookie: 读取所有的cookie: document.cookie: 读取单个cookie:$.cookie('cookiename'); 新增cookie: $.cookie(' ...
- easyui combogrid下拉表格的分页/按键/动态搜索
作者:xfl4629712 < easyui combogrid下拉表格的分页/按键/动态搜索 > 需求: 1.下拉框下拉时出现表格: 2.表格带分页功能: 3.可以使用向上键.向下 ...
- Confluence 6 复杂授权或性能问题
提交一个 服务器请求(support request) 然后在你的服务请求中同时提供下面的信息. Confluence 服务器 登录 Confluence 然后访问管理员控制台. 将 系统信息(Sys ...
- Confluence 6 连接到外部用户目录服务器的问题分析
在有关外部目录服务器配置页面中有一个测试配置(Test Settings)按钮.这个功能将会帮助你分析你的用户管理在 Active Directory 和其他 LDAP 服务器中出现的问题. 希望对你 ...
- centos7查看yum安装的软件及路径
rpm -qa 查看所有已安装软件名称 rpm -ql 软件名 显示软件的安装路径
- ionic3 打包报错[ERROR] An error occurred while running cordova prepare (exit code 1):
解决办法:删除并重新添加平台以使用以下命令解决问题: cordova platform rm ios cordova platform add ios 如果执行 ionic cordova build ...
- Redis创建集群报错
Redis创建集群报错: 1:任何一个集群节点中都不能存在数据,如果有备份一下删除掉aof文件或rdb文件 2: nodes-集群端口.conf 文件存的会有报错记录,所以该文件也要删除
- python 垃圾回收
# 垃圾回收 # 小整数对象池 # a = 100# python对小整数的定义是[-5,257],这些证书对象是提前创建好的,不会被垃圾回收,再一个python的程序中,所有位于这个范围内的正式使用 ...
- NodeJs——router报错原因
rout.js var http = require('http'); var url = require('url'); var router = require('./models/router. ...