集群现在越来越常见,当我们项目搭建了集群,就会产生session共享问题。因为session是保存在服务器上面的。那么解决这一问题,大致有三个方案,1.通过nginx的负载均衡其中一种ip绑定来实现(通过ip绑定服务器其中一台,就没有集群概念了);2.通过cookie备份session实现(因为cookie数据保存在客户端,不推荐;3.通过redis备份session实现(推荐);

学习本章节之前,建议依次阅读以下文章,更好的串联全文内容,如已掌握以下列出知识点,请跳过:

vSpring Session概念

Spring Session 提供了一套创建和管理 Servlet HttpSession 的方案。Spring Session 提供了集群 Session(Clustered Sessions)功能,默认采用外置的 Redis 来存储 Session 数据(不用手动存储到redis中),以此来解决 Session 共享的问题。

Spring Session 为企业级 Java 应用的 session 管理带来了革新,使得以下的功能更加容易实现:

API 和用于管理用户会话的实现。
支持每个浏览器上使用多个 session,从而能够很容易地构建更加丰富的终端用户体验。
当用户使用 WebSocket 发送请求的时候,能够保持 HttpSession 处于活跃状态。
控制 session id 如何在客户端和服务器之间进行交换,这样的话就能很容易地编写 Restful API,因为它可以从 HTTP头信息中获取 session id,而不必再依赖于 cookie。
将 session 所保存的状态卸载到特定的外部 session 存储中,如 Redis 或 Apache Geode 中,它们能够以独立于应用服务器的方式提供高质量的集群。
HttpSession - 允许以应用程序容器(即 Tomcat)中性的方式替换 HttpSession。

关于更多Spring Session概念的介绍,可以看看官网Spring Session

vSpring Session整合

1.1 引入pmo.xml

        <dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>

1.2 创建useraccount表

CREATE TABLE `useraccount` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`age` int(10) NOT NULL,
`phone` bigint NOT NULL,
`email` varchar(255) NOT NULL,
`account` varchar(100) NOT NULL UNIQUE,
`pwd` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
insert into `useraccount` values(1,'赵(dev)',23,158,'3658561548@qq.com','test001','test001');
insert into `useraccount` values(2,'钱(dev)',27,136,'3658561548@126.com','test002','test002');
insert into `useraccount` values(3,'孙(dev)',31,159,'3658561548@163.com','test003','test003');
insert into `useraccount` values(4,'李(dev)',35,130,'3658561548@sina.com','test004','test004');
select * from `useraccount`;

1.3 mapper

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.demo.dao.UserAccountMapper">
<resultMap id="BaseResultMap" type="com.demo.pojo.UserAccount">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="username" jdbcType="VARCHAR" property="username" />
<result column="age" jdbcType="INTEGER" property="age" />
<result column="phone" jdbcType="BIGINT" property="phone" />
<result column="email" jdbcType="VARCHAR" property="email" />
<result column="account" jdbcType="VARCHAR" property="account" />
<result column="pwd" jdbcType="VARCHAR" property="pwd" />
</resultMap>
<sql id="Base_Column_List">
id, username, age, phone, email, account, pwd
</sql>
<select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from useraccount
where id = #{id,jdbcType=INTEGER}
</select>
<select id="getUserByAccount" parameterType="java.lang.String" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from useraccount
where account = #{account}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
delete from useraccount
where id = #{id,jdbcType=INTEGER}
</delete>
<insert id="insert" parameterType="com.demo.pojo.UserAccount">
insert into useraccount (id, username, age,
phone, email, account,
pwd)
values (#{id,jdbcType=INTEGER}, #{username,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER},
#{phone,jdbcType=BIGINT}, #{email,jdbcType=VARCHAR}, #{account,jdbcType=VARCHAR},
#{pwd,jdbcType=VARCHAR})
</insert>
<insert id="insertSelective" parameterType="com.demo.pojo.UserAccount">
insert into useraccount
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="username != null">
username,
</if>
<if test="age != null">
age,
</if>
<if test="phone != null">
phone,
</if>
<if test="email != null">
email,
</if>
<if test="account != null">
account,
</if>
<if test="pwd != null">
pwd,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=INTEGER},
</if>
<if test="username != null">
#{username,jdbcType=VARCHAR},
</if>
<if test="age != null">
#{age,jdbcType=INTEGER},
</if>
<if test="phone != null">
#{phone,jdbcType=BIGINT},
</if>
<if test="email != null">
#{email,jdbcType=VARCHAR},
</if>
<if test="account != null">
#{account,jdbcType=VARCHAR},
</if>
<if test="pwd != null">
#{pwd,jdbcType=VARCHAR},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.demo.pojo.UserAccount">
update useraccount
<set>
<if test="username != null">
username = #{username,jdbcType=VARCHAR},
</if>
<if test="age != null">
age = #{age,jdbcType=INTEGER},
</if>
<if test="phone != null">
phone = #{phone,jdbcType=BIGINT},
</if>
<if test="email != null">
email = #{email,jdbcType=VARCHAR},
</if>
<if test="account != null">
account = #{account,jdbcType=VARCHAR},
</if>
<if test="pwd != null">
pwd = #{pwd,jdbcType=VARCHAR},
</if>
</set>
where id = #{id,jdbcType=INTEGER}
</update>
<update id="updateByPrimaryKey" parameterType="com.demo.pojo.UserAccount">
update useraccount
set username = #{username,jdbcType=VARCHAR},
age = #{age,jdbcType=INTEGER},
phone = #{phone,jdbcType=BIGINT},
email = #{email,jdbcType=VARCHAR},
account = #{account,jdbcType=VARCHAR},
pwd = #{pwd,jdbcType=VARCHAR}
where id = #{id,jdbcType=INTEGER}
</update>
</mapper>
package com.demo.dao;

import com.demo.pojo.UserAccount;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository; @Mapper
@Repository
public interface UserAccountMapper {
int deleteByPrimaryKey(Integer id); int insert(UserAccount record); int insertSelective(UserAccount record); UserAccount selectByPrimaryKey(Integer id); UserAccount getUserByAccount(@Param("account") String account); int updateByPrimaryKeySelective(UserAccount record); int updateByPrimaryKey(UserAccount record);
}

1.4 实体类

package com.demo.pojo;

import java.io.Serializable;

public class UserAccount  implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id; private String username; private Integer age; private Long phone; private String email; private String account; private String pwd; public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username == null ? null : username.trim();
} public Integer getAge() {
return age;
} public void setAge(Integer age) {
this.age = age;
} public Long getPhone() {
return phone;
} public void setPhone(Long phone) {
this.phone = phone;
} public String getEmail() {
return email;
} public void setEmail(String email) {
this.email = email == null ? null : email.trim();
} public String getAccount() {
return account;
} public void setAccount(String account) {
this.account = account == null ? null : account.trim();
} public String getPwd() {
return pwd;
} public void setPwd(String pwd) {
this.pwd = pwd == null ? null : pwd.trim();
}
}

1.5 service层

Service

UserAccount getUserByAccount(String account);

ServiceImpl

    @Override
public UserAccount getUserByAccount(String account){
return userAccountMapper.getUserByAccount(account);
}

1.6 添加session配置类

package com.demo.common;

import org.springframework.context.annotation.Configuration;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; /**
* Created by toutou on 2019/1/22.
*/
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)
/**
* maxInactiveIntervalInSeconds: 设置 Session 失效时间,使用 Redis Session 之后,原 Boot 的 server.session.timeout 属性不再生效。
*/
public class SessionRedisConfig {
}

1.7 设置拦截器

关于拦截器不懂的话,可以看我之前的一篇文章《SpringBoot(十一)过滤器和拦截器》

    /*
* 进入controller层之前拦截请求
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object obj) throws Exception {
System.out.println("MyTestInterceptor 1111111111");
Object user= request.getSession().getAttribute("useraccount");
if (user==null){
// response.sendRedirect(request.getContextPath()+"/error");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("请先登录");
return false;
} return true;
}
    @Override
public void addInterceptors(InterceptorRegistry registry) {
// 自定义拦截器,添加拦截路径和排除拦截路径
registry.addInterceptor(myTestInterceptor).addPathPatterns("/**").excludePathPatterns("/userlogin");
}

设置全局拦截

1.8 登录/注销

package com.demo.controller;

import com.demo.pojo.UserAccount;
import com.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession; /**
* Created by toutou on 2019/1/22.
*/
@RestController
public class LoginController {
@Autowired
UserService userService; @RequestMapping(value = "/userlogin")
public String Login(HttpServletRequest request, String account, String pwd)
{
UserAccount user= userService.getUserByAccount(account);
if (user!=null && user.getPwd().equals(pwd)){
request.getSession().setAttribute("useraccount",user);
return "登录成功";
} return "登录失败";
} @RequestMapping(value = "/userlogout")
public String logout (HttpServletRequest request){
HttpSession session=request.getSession();
session.removeAttribute("useraccount");
return "退出登录";
}
}

v效果演示

2.1 直接访问页面

提示:请先登录

2.2 调用登录接口

登录以后再次访问页面,提示访问成功。

2.3 退出登录

v源码地址

https://github.com/toutouge/javademosecond/tree/master/hellospringboot

作  者:请叫我头头哥

出  处:http://www.cnblogs.com/toutou/

关于作者:专注于基础平台的项目开发。如有问题或建议,请多多赐教!

版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

特此声明:所有评论和私信都会在第一时间回复。也欢迎园子的大大们指正错误,共同进步。或者直接私信

声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是作者坚持原创和持续写作的最大动力!

SpringBoot进阶教程(二十六)整合Redis之共享Session的更多相关文章

  1. SpringBoot进阶教程(二十九)整合Redis 发布订阅

    SUBSCRIBE, UNSUBSCRIBE 和 PUBLISH 实现了 发布/订阅消息范例,发送者 (publishers) 不用编程就可以向特定的接受者发送消息 (subscribers). Ra ...

  2. SpringBoot进阶教程(二十八)整合Redis事物

    Redis默认情况下,事务支持被禁用,必须通过设置setEnableTransactionSupport(true)为使用中的每个redistplate显式启用.这样做会强制将当前重新连接绑定到触发m ...

  3. SpringBoot进阶教程(二十五)整合Redis之@Cacheable、@CachePut、@CacheEvict的应用

    在上一篇文章(<SpringBoot(二十四)整合Redis>)中,已经实现了Spring Boot对Redis的整合,既然已经讲到Cache了,今天就介绍介绍缓存注解.各家互联网产品现在 ...

  4. SpringBoot进阶教程(二十四)整合Redis

    缓存现在几乎是所有中大型网站都在用的必杀技,合理的利用缓存不仅能够提升网站访问速度,还能大大降低数据库的压力.Redis提供了键过期功能,也提供了灵活的键淘汰策略,所以,现在Redis用在缓存的场合非 ...

  5. SpringBoot进阶教程(五十九)整合Codis

    上一篇博文<详解Codis安装与部署>中,详细介绍了codis的安装与部署,这篇文章主要介绍介绍springboot整合codis.如果之前看过<SpringBoot进阶教程(五十二 ...

  6. SpringBoot进阶教程(七十四)整合ELK

    在上一篇文章<SpringBoot进阶教程(七十三)整合elasticsearch >,已经详细介绍了关于elasticsearch的安装与使用,现在主要来看看关于ELK的定义.安装及使用 ...

  7. SpringBoot进阶教程(二十二)集成RabbitMQ---MQ实战演练

    RabbitMQ是一个在AMQP基础上完成的,可复用的企业消息系统.他遵循Mozilla Public License开源协议.RabbitMQ是流行的开源消息队列系统,用erlang语言开发.Rab ...

  8. SpringBoot进阶教程(二十七)整合Redis之分布式锁

    在之前的一篇文章(<Java分布式锁,搞懂分布式锁实现看这篇文章就对了>),已经介绍过几种java分布式锁,今天来个Redis分布式锁的demo.redis 现在已经成为系统缓存的必备组件 ...

  9. Android高手进阶教程(二十八)之---Android ViewPager控件的使用(基于ViewPager的横向相册)!!!

      分类: Android高手进阶 Android基础教程 2012-09-14 18:10 29759人阅读 评论(35) 收藏 举报 android相册layoutobjectclassloade ...

随机推荐

  1. Java开源生鲜电商平台-盈利模式详解(源码可下载)

    Java开源生鲜电商平台-盈利模式详解(源码可下载) 该平台提供一个联合买家与卖家的一个平台.(类似淘宝购物,这里指的是食材的购买.) 平台有以下的盈利模式:(类似的平台有美菜网,食材网等) 1. 订 ...

  2. OpenApi开放平台架构实践

    背景 随着业务的发展,越来越多不同系统之间需要数据往来,我们和外部系统之间产生了数据接口的对接.当然,有我们提供给外部系统(工具)的,也有我们调用第三方的.而这里重点讲一下我们对外的接口. 目前,我们 ...

  3. IAAS-虚拟化技术组件介绍

    虚拟化技术组件涉及众多,下面对一些组件所处的层级以及定位做个简单的汇总介绍,部分信息来自于网络整理,如有不准确之处,请指正.

  4. iscsi 挂载网络存储及存储访问

    http://blog.sina.com.cn/s/blog_408764940101ghzi.html 一.Ess3016x设置 登陆admin 密码 888888888888 1.安装硬盘,查看硬 ...

  5. hexo+github创建属于自己的博客

    配置环境 安装Node(必须) 作用:用来生成静态页面的 到Node.js官网下载相应平台的最新版本,一路安装即可. 安装Git(必须) 作用:把本地的hexo内容提交到github上去. 安装Xco ...

  6. USB/GPIO/SDIO

    通用串行总线(英语:Universal Serial Bus,缩写:USB)是连接计算机系统与外部设备的一种串口总线标准,也是一种输入输出接口的技术规范.它只有4根线,两个电源(5V,地线),两个数据 ...

  7. Kali Linux图形界面与命令行界面的切换

    前言: 有时候为了节约系统资源,使用命令行界面能够让Linux系统的运行更加流畅,处理任务的速度也更加快.现在的Kali安装完成后默认是开机就启动图形化界面.本文将介绍通过编辑Kali启动引导文件的方 ...

  8. killall 、kill 、pkill 命令详解

    killall 命令 Linux系统中的killall命令用于杀死指定名字的进程(kill processes by name).我们可以使用kill命令杀死指定进程PID的进程,如果要找到我们需要杀 ...

  9. Python web简约表白网页源码分享,时光不老,我们不散!

    演示站:c.lmz8.cn打开js/4.js,复制到工具箱的js代码整理那,先解密,方便查看.工具箱:tool.lmz8.cnjs代码整理.在线解码 这个便是文字,只不过呗转码了,所以要用到解码工具. ...

  10. Maven常识

    maven下面通常有四个文件夹: src/main/java -- 用来存放业务代码 src/test/java -- 用来存放测试代码 另有两个名为resource的文件夹,通常用来放置前两个文件夹 ...