环境: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的更多相关文章

  1. Redis与Spring Data Redis

    1.Redis概述 1.1介绍 官网:https://redis.io/ Redis是一个开源的使用ANSIC语言编写.支持网络.可基于内存 亦可持久化的日志型.Key-Value型的高性能数据库. ...

  2. Redis(八):spring data redis 理解

    前言 Spring Data Redis project,应用了Spring概念来开发使用键值形式的数据存储的解决方案.我们(官方)提供了一个 "template" ,这是一个高级 ...

  3. Redis客户端 Spring Data Redis(未完)

    官网:http://projects.spring.io/spring-data-redis/ 1.0  参考之前的一片文章:Gradle入门实战(Windows版) 构建java applicati ...

  4. Spring Data Redis整体介绍 (一)

    为什么使用Spring Data Redis 首先Spring Data Redis 是Spring 框架提供的用于操作Redis的客户端. Spring框架是一个全栈Java程序框架,通过DI.AO ...

  5. spring boot通过Spring Data Redis集成redis

    在spring boot中,默认集成的redis是Spring Data Redis,Spring Data Redis针对redis提供了非常方便的操作模版RedisTemplate idea中新建 ...

  6. Spring Data Redis 让 NoSQL 快如闪电(2)

    [编者按]本文作者为 Xinyu Liu,文章的第一部分重点概述了 Redis 方方面面的特性.在第二部分,将介绍详细的用例.文章系国内 ITOM 管理平台 OneAPM 编译呈现. 把 Redis ...

  7. Spring Data Redis学习

    本文是从为知笔记上复制过来的,懒得调整格式了,为知笔记版本是带格式的,内容也比这里全.点这里 为知笔记版本 Spring Data Redis 学习 Version 1.8.4.Release 前言 ...

  8. Spring Boot使用Spring Data Redis操作Redis(单机/集群)

    说明:Spring Boot简化了Spring Data Redis的引入,只要引入spring-boot-starter-data-redis之后会自动下载相应的Spring Data Redis和 ...

  9. 使用Spring Data Redis操作Redis(集群版)

    说明:请注意Spring Data Redis的版本以及Spring的版本!最新版本的Spring Data Redis已经去除Jedis的依赖包,需要自行引入,这个是个坑点.并且会与一些低版本的Sp ...

  10. 使用Spring Data Redis操作Redis(单机版)

    说明:请注意Spring Data Redis的版本以及Spring的版本!最新版本的Spring Data Redis已经去除Jedis的依赖包,需要自行引入,这个是个坑点.并且会与一些低版本的Sp ...

随机推荐

  1. ProtocolError: <ProtocolError for 127.0.0.1/RPC2: 401 Unauthor.

    安装cloudera-manager-agent报错 查看/var/log/cloudera-scm-agent.log 报错 Traceback (most recent call last): F ...

  2. Confluence 6 跟踪你安装中的自定义修改

    在 Confluence 中的系统信息(System Information)部分,有一个 修改(Modification)的选项.在这个选项中列出了自你 Confluence 安装以来,你 Conf ...

  3. Confluence 6 让 Jira 应用停止发送通知到 Confluence

    你可能希望仅仅配置 Confluence 在 workbox 中仅仅显示自己的通知,禁用在 workbox 中显示从 Jira 来的通知.有可能这个 Jira 的应用已经通过应用链接功能正确链接到 C ...

  4. UserNotifications ios10 通知使用

    通知在ios10 中推荐使用 导入  import UserNotifications  头文件 if #available(iOS 10.0, *) { UNUserNotificationCent ...

  5. 对于stark(curd)插件的使用简单介绍

    一.创建表 from django.db import models from django.db import models class Department(models.Model): &quo ...

  6. 正则化 L1 L2

    机器学习中几乎都可以看到损失函数后面会添加一个额外项,常用的额外项一般有两种,一般英文称作ℓ1ℓ1-norm和ℓ2ℓ2-norm,中文称作L1正则化和L2正则化,或者L1范数和L2范数. L1正则化和 ...

  7. PDF文件编辑技巧之PDF书签怎么设置

    纸质的文件想要添加书签就直接拿笔书写就可以,Word文件怎么添加书签相信大家也都知道,那么PDF文件的书签要怎么设置的呢,是不是有很多小伙伴不知道该怎么做呢,不要担心,今天小编就来跟大家分享一下在PD ...

  8. Axure实现多用户注册验证

    *****多用户登录验证***** 一.(常规想法)方法:工作量较大,做起来繁琐 1.当用户名和密码相同时怎么区分两者,使用冒号和括号来区分: eg. (admin:123456)(123456:de ...

  9. Google C++ 代码规范

    Google C++ Style Guide   Table of Contents Header Files Self-contained Headers The #define Guard For ...

  10. Github版本管理以及git使用

    1.git客户端编译安装 同步系统时间服务器 ntpdate cn.ntp.org.cn 安装依赖包: [root@baolin ~]# yum install epel-release -y [ro ...