前言
之前通过 docker 在本地搭建了 Redis 环境、而公司内部 Session 方案有诸多定制,同时通过中间件也对 redis 的操作进行了大量封装。
为了一探究竟,本文通过构建一个简单的 web 应用,将 Session 管理与 Redis 的使用相结合从而加深理解。
术语
Session:web应用中用于表示客户端与服务器之间保持状态的解决方案。
Cookie:由web服务器生成发往客户端存储的数据、通常用于记录客户端登陆信息、方便服务端判断请求是否来源于相同的客户端。
HttpSession:因为 HTTP 协议无状态、为了在服务器端实现状态管理Java平台制定了 Session 标准接口规范了服务端状态管理的基本操作。
Web Server:通常也称为 web 容器如 apache tomcat,eclipse jetty,oracle weblogic。它们都实现了 HttpSession 接口、具体细节略有差异。
Spring Session:HttpSession 接口在 Spring Framework 中的实现。通过 Spring Session 将 Session 管理与特定的 web server 解藕、提供统一的 session 管理方案。
Spring Boot 集成 Spring Session
Spring Session 实现了 HttpSession 接口替换了 web server的默认实现,下面将在 demo 应用创建过程中说明其内部机制。
创建 Spring Boot web 应用
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.kongfutech</groupId>
<artifactId>spring-session-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-session-demo</name>
<description>Demo project for Spring Session with Redis</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
集成 Spring Session Data Redis
在 pom 文件中增加 spring-session-data-redis 依赖,由于 Spring Boot 已经集成了 Spring Session 所以这里不再单独引入。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
在 application.properties 中配置以 Redis 作为 Spring Session 存储方式。除 Redis 外 Spring Session 目前还支持 JDBC、MongoDB 等其他存储方式。同时需要配置 redis 连接信息。
server.port=8081
# Session store type.
spring.session.store-type=redis
# Session timeout. If a duration suffix is not specified, seconds is used.
server.servlet.session.timeout=60s
# Redis server host.
spring.redis.host=localhost
# Login password of the redis server.
spring.redis.password=
# Redis server port.
spring.redis.port=6379
# Sessions flush mode.
spring.session.redis.flush-mode=on-save
# Namespace for keys used to store sessions.
spring.session.redis.namespace=springboot-session
增加上述配置之后 Spring Boot 会创建一个名为 springSessionRepositoryFilter 的 SessionRepositoryFilter 去替换 web server 中 HttpSesion 实现。这样就将 Session 的管理交给了 Spring Session。若将 spring.session.store-type 设置为空则表示在应用中禁用 Spring Session。
Spring Boot 会使用 Redis 连接配置自动创建 RedisConnectionFactory 负责与 Redis 集群的交互。
创建 REST Controller
package com.kongfutech.springsession.controller;
import javax.servlet.http.HttpSession;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author kongfutech
* @version sessionDemo: SessionDemo.java, v 0.1 2019-06-24 19:23 kongfutech Exp $
*/
@RestController
public class SessionDemo {
@GetMapping("/")
String sessionId(HttpSession session) {
return session.getId();
}
}
配置 Spring Security
package com.kongfutech.springsession.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* @author kongfutech
* @version securityConfig: SecurityConfig.java, v 0.1 2019-06-24 19:26 kongfutech Exp $
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// Authentication : Admin --> Roles
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().passwordEncoder(
org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance()).withUser("admin").password("haha").roles("ADMIN");
}
// Authorization : Role -> Access
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().authorizeRequests().antMatchers("/").hasRole("ADMIN").anyRequest()
.authenticated();
}
}
使用 postman 测试
未登陆情况下访问服务
使用密码访问服务
通过 redis-cli 查看 Session 信息。
➜:~ kongfutech$ redis-cli keys "*" | xargs redis-cli del
Spring MVC 集成 Spring Session
集成 Spring Session Data Redis
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>{spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>{spring-session-version}}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>5.0.4.RELEASE</version>
</dependency>
Spring boot 通过 spring.session.store-type=redis 这种配置方式实际等价于使用 @EnableRedisHttpSession 创建 filter 替换 Tomcat 等容器默认 Session 实现。
@Configuration
@EnableRedisHttpSession
public class HttpSessionConfig extends AbstractHttpSessionApplicationInitializer {
// 配置 Redis 连接实现 Session 信息的存储
@Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory(new RedisStandaloneConfiguration("127.0.0.1", 6379));
}
}
总结
-
Spring boot 应用通过在 pom 文件增加相关依赖实现了 Spring Session 集成,同时在 application.properties 中启用或关闭 Redis 存储 Session。
-
Spring Session 通过创建一个名为 springSessionRepositoryFilter 的 SessionRepositoryFilter 替换了 web server 默认的 HttpSession 实现。从而实现了 Session 管理与特定的 web server 的解藕以及 Session 存储方式的扩展。
-
非 Spring boot 应用集成方式略有不同、但其实现原理是一致的。