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/
關於作者:專注於基礎平台的項目開發。如有問題或建議,請多多賜教!
版權聲明:本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。
特此聲明:所有評論和私信都會在第一時間回復。也歡迎園子的大大們指正錯誤,共同進步。或者直接私信
聲援博主:如果您覺得文章對您有幫助,可以點擊文章右下角推薦一下。您的鼓勵是作者堅持原創和持續寫作的最大動力!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM