SpringBoot(二十六)整合Redis之共享Session

Posted 请叫我头头哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot(二十六)整合Redis之共享Session相关的知识,希望对你有一定的参考价值。

集群现在越来越常见,当我们项目搭建了集群,就会产生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

SpringBoot进阶教程(二十六)整合Redis之共享Session
<?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>
View Code
SpringBoot进阶教程(二十六)整合Redis之共享Session
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);
}
View Code

1.4 实体类

SpringBoot进阶教程(二十六)整合Redis之共享Session
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();
    }
}
View Code

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 直接访问页面

SpringBoot进阶教程(二十六)整合Redis之共享Session

提示:请先登录

2.2 调用登录接口

SpringBoot进阶教程(二十六)整合Redis之共享Session

SpringBoot进阶教程(二十六)整合Redis之共享Session

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

SpringBoot进阶教程(二十六)整合Redis之共享Session

SpringBoot进阶教程(二十六)整合Redis之共享Session

2.3 退出登录

SpringBoot进阶教程(二十六)整合Redis之共享Session

v源码地址

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


作  者:请叫我头头哥
出  处:http://www.cnblogs.com/toutou/
关于作者:专注于基础平台的项目开发。如有问题或建议,请多多赐教!
版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
特此声明:所有评论和私信都会在第一时间回复。也欢迎园子的大大们指正错误,共同进步。或者直接私信
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是作者坚持原创和持续写作的最大动力!

以上是关于SpringBoot(二十六)整合Redis之共享Session的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot(二十七)整合Redis之分布式锁

SpringBoot(二十四)整合Redis

SpringBoot入门二十三,整合Redis

lettuce之springboot整合redis

企业分布式微服务云SpringCloud SpringBoot mybatis (二十三)整合Redis

Redis(二十六)-持久化操作之AOF