Spring Session:入门案例

Spring Session provides an API and implementations for managing a user’s session information.

Spring Session提供了一种用于管理用户session信息管理的API。

Spring Session特点

         传统的Servlet应用中,Session是存储在服务端的,即:Session信息与特定的Web服务相互绑定。Spring Session提供了一种支持分布式session的解决方案,而无需与特定应用容器相绑定。

  • HttpSession:支持以应用容器中立的方式替换HttpSession,支持在Http请求头中提供session IDs,以便与Restful API配合使用。
  • WebSocket:提供了WebSocket消息通信中保持HttpSession会话的能力。
  • WebSession:允许以应用容器中立的方式替换 Spring WebFlux 的 WebSession。

核心模块

        Spring Session由以下模块组成,

  • Spring Session Core:提供Spring Session 核心功能和 API
  • Spring Session Data Redis:提供由 Redis 支持的 SessionRepository 和 ReactiveSessionRepository 实现,以及配置支持
  • Spring Session JDBC:提供由关系型数据库支持的SessionRepository实现,以及配置支持
  • Spring Session Hazelcast:提供由Hazelcast支持的SessionRepository实现,以及配置支持
  • Spring Session MongoDB:提供由MongoDB支持的SessionRepository实现,以及配置支持

Spring Session解决的问题

        当用户和web应用交互时,服务端创建一个Session去追踪用户的操作。这个Session可以存储与用户相关的信息,例如:用户行为,登录状态,购物车内容等等。但是,Session在集群环境中存在问题,因为session被存储在服务内存中。

        如上如所示,假定:Spring App#2接收到了session #3,该服务端应用无法获取到session数据,因为Spring App#1和Spring App#2的session信息是独立存储在各自的应用内存中的。这对集群环境是一个问题。

        为了解决这个问题,我们必须想办法实现session共享,

         如上图所示,通过session共享,可以使得每个应用都能访问到session存储信息。Spring Session正是在应用和session管理之间提供了一个抽象层,将session数据存储在各种持久化存储中,例如:关系型/非关系型数据库中。

        通过SpringSession,你可以使用相同的API去管理session,而无需例会session是如何进行持久化存储的,同时也可以无痛的切换持久化存储方案且无需改动任何代码。

        此外,SpringSession也提供了session过期和不同web应用之间的跨上下文通信机制。

Spring Session简化了web应用的session管理,使得开发者可以专注于构建应用程序的核心功能。

Spring Session的应用场景

        以下是Spring Session的一些应用案例:

  • 分布式web应用:如果网络应用程序分布在多个服务器上,那么管理用户会话可能会很困难。Spring Session 可以将会话数据存储在共享数据库或 Redis 中,让所有服务器都能访问和更新会话数据。
  • 会话拓展能力:在用户并发量较高的大型web应用中,将会话信息存储在服务器内存中会导致可拓展性问题。Spring Session 可以将会话数据存储在持久化存储中,从而提高可扩展性并降低内存溢出的风险。
  • session备份和恢复:将会话数据存储在持久性存储中,还能在服务器出现故障或停机时提供备份和恢复会话数据的机制。

Quick Start

        官网文档:Samples and Guides (Start Here) :: Spring Session

        以下使用Spring Session Data Redis核心模块,实现一个Spring Session快速入门案例。

引入依赖

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

        可以看到Spring session data redis内置了spring data redis依赖,

配置session存储类型

        在application.proeprties文件中,配置session数据存储类型为Redis,

#session store type
spring.session.store-type=redis

        相当于为SpringBoot应用添加了@EnableRedisHttpSession注解,

        得益于SpringBoot autoConfigure自动装配的特点,我们可以进行少量的配置即可。此处我们可以去查看一下spring session自动装配的类为SessionAutoConfiguration

org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,

        点进去看一下自动装配生效的条件是:Session接口存在,

        我们已经引入了Spring Session相关的依赖,那么Session接口自然是存在的。

其它配置

        此外,我们还可以配置如下信息,会话过期时间、Session刷新模式、redis-session命名空间,

server.port=8080

#session store type
spring.session.store-type=redis
# Session timeout(单位为秒s). If a duration suffix is not specified, seconds is used.(默认为30min)
server.servlet.session.timeout=3600
# Sessions flush mode.(默认为ON_SAVE)
spring.session.redis.flush-mode=on_save
# Namespace for keys used to store sessions.(默认为spring:session)
spring.session.redis.namespace=spring:session

Redis连接配置

        Spring Boot自动创建了RedisConnectionFactory用于将spring Session连接到端口为6379的Redis Server。在生产环境中,建议更新Redis服务的配置端点信息。

# redis
spring.redis.host=localhost
spring.redis.password=
spring.redis.port=6379

注解:启用Redis-Session存储

        在启动类上使用@EnableRedisHttpSession注解,其作用是:用于创建一个名为 springSessionRepositoryFilter 的 Spring Bean,它实现了Filter过滤器。该过滤器负责替换 HttpSession 实现,使其由 Spring Session 支持。简单讲:就是

@SpringBootApplication
@EnableRedisHttpSession
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

案例测试

        以上步骤完成之后,启动SpringBoot应用成功,其实就已经整合完成了。但是却感受不到任何效果。以下我们从一个案例感受一下Session存储用户信息的作用。

案例描述

创建两个接口:

①app/addUser?id=XX:创建一个id为指定XX值的用户,并将用户信息存储到Session中

②/app/getUser?id=XX:用于从Session中获取指定id的用户信息

SysUser类

package com.example.demo.model.entity;

import java.io.Serializable;

public class SysUser implements Serializable {
    private static final long serialVersionUID = -1802561599630491376L;
    private Integer id;
    private String name;
    private Integer age;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public SysUser() {
    }

    public SysUser(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "SysUser{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

创建接口

package com.example.demo.web;

import com.example.demo.model.entity.SysUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpSession;

@Slf4j
@RestController
@RequestMapping(value = "/app")
public class AppController {

    //接收一个id,创建user并返回
    @GetMapping("/addUser")
    public SysUser addUser(HttpSession session,
                           @RequestParam(value = "id")Integer id){
        SysUser user = new SysUser(id, "张三", 18);
        //设置user信息到session中
        session.setAttribute(String.valueOf(id),user);
        log.info("create SysUser:{}",user);
        return user;
    }

    //接收一个id,获取Session中存储的user信息
    @GetMapping("/getUser")
    public SysUser getUser(HttpSession session,
                           @RequestParam(value = "id")Integer id){
        SysUser user = (SysUser) session.getAttribute(String.valueOf(id));
        log.info("get SysUser:{}",user);
        return user;
    }

}

 测试接口

(1)app/addUser?id=1,创建一个用户,

        此时查看redis数据库,会看到对应的信息,

 (2)获取用户信息:/app/getUser?id=1,

        查看请求头和响应头中,会看到cookie中已经产生了SESSION键,

 

分布式案例测试

        以上只是单体应用,我们的访问接口都是8080,我们重新启动一个8001的web应用,然后访问getUser接口,看看能不能访问到session中存储的用户信息。

        启动8001端口的应用,访问接口,可看到成功访问到用户信息,

         另外,我们对比Cookie中的SESSION值,也是相同的,

        加入我们更换浏览器访问呢?可以看到SESSION对应的值变了,接口响应200正常,但是返回接口为null,找不到对应的用户信息,这和我们的预期是一致的。

 

        此时查看Redis数据库,会发现多了一条SESSION会话信息,

 

  • 18
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是席木木啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值