On this page
Spring Session
Spring Session 提供了用于 Management 用户会话信息的 API 和实现。
Introduction
Spring Session 提供了用于 Management 用户会话信息的 API 和实现。它还提供了透明的集成:
HttpSession-允许以应用程序容器(即 Tomcat)中立的方式替换 HttpSession。其他功能包括:
集群会话 -Spring Session 使得支持clustered sessions变得很简单,而不必依赖于特定于应用程序容器的解决方案。
多个浏览器会话 -Spring Session 在单个浏览器实例(即类似于 Google 的多个经过身份验证的帐户)中支持Management 多个用户的会话。
RESTful APIs -Spring Session 允许在 Headers 中提供会话 ID 以与RESTful APIs一起使用
WebSocket-能够在接收 WebSocket 消息时使
HttpSession保持活动状态
1.3 的新功能
以下是 Spring Session 1.3 新增功能的重点。您可以通过参考1.3.0.M1,1.3.0.M2,1.3.0.RC1和1.3.0.RELEASE的更改日志找到新功能的完整列表。
Hazelcast的一流支持
Added OrientDB 社区扩展
GenericJackson2JsonRedisSerializer sample带有 Spring Security 的新 Jackson 支持
现在的指南use Lettuce
spring.session.cleanup.cron.expression可用于覆盖清理任务的 cron 表达式许多性能改进和错误修复
示例和指南(从这里开始)
如果您希望开始使用 Spring Session,最好的起点是我们的示例应用程序。
表 1.示例应用程序
| Source | Description | Guide |
|---|---|---|
| HttpSession | 演示如何使用 Spring Session 用 Redis 存储替换HttpSession。 |
HttpSession Guide |
| HttpSession XML | 演示如何使用 Spring Session 通过基于 XML 的配置用 Redis 存储替换HttpSession。 |
HttpSession XML 指南 |
| 使用 Spring Boot 与 GemFire 进行 HttpSession | 演示如何在 Spring Boot 应用程序中使用 Client 端/服务器拓扑使用 Spring Session 将HttpSession替换为 GemFire。 |
HttpSession GemFire 启动指南 |
| 带有 GemFire 的 HttpSession(Client 端/服务器) | 演示如何使用 Spring Session 通过 Client 端/服务器拓扑结构将HttpSession替换为 GemFire。 |
HttpSession GemFireClient 端/服务器指南 |
| 使用 XML 的 HttpSession 与 GemFire(Client 端/服务器) | 演示如何使用 Spring Session 通过配置有 XML 的 Client 端/服务器拓扑,使用 GemFire 将HttpSession替换为 GemFire。 |
HttpSession GemFireClient 端/服务器 XML 指南 |
| 具有 GemFire(P2P)的 HttpSession | 演示如何使用 Spring Session 通过 P2P 拓扑将 G_Fire 替换为HttpSession。 |
HttpSession GemFire P2P 指南 |
| 使用 XML 的 HttpSession 与 GemFire(P2P) | 演示如何使用 Spring Session 通过配置有 XML 的 P2P 拓扑将 G_Fire 替换为HttpSession。 |
HttpSession GemFire P2P XML 指南 |
| Custom Cookie | 演示如何使用 Spring Session 和自定义 cookie。 | 自定义 Cookie 指南 |
| Spring Boot | 演示如何在 Spring Boot 中使用 Spring Session。 | Spring 启动指南 |
| Grails 3 | 演示如何在 Grails 3 中使用 Spring Session。 | Grails 3 指南 |
| Spring Security | 演示如何将 Spring Session 与现有的 Spring Security 应用程序一起使用。 | Spring Security 指南 |
| REST | 演示如何在 REST 应用程序中使用 Spring Session 支持通过 Headers 进行身份验证。 | REST Guide |
| 按用户名查找 | 演示如何使用 Spring Session 通过用户名查找会话。 | 按用户名查找 |
| Multiple Users | 演示如何使用 Spring SessionManagement 多个同时的浏览器会话(即 Google 帐户)。 | Management 多个用户指南 |
| WebSocket | 演示如何将 Spring Session 与 WebSockets 结合使用。 | WebSocket Guide |
| Mongo | 演示如何在 Mongo 中使用 Spring Session。 | Mongo Guide |
| Hazelcast | 演示如何将 Spring Session 与 Hazelcast 结合使用。 | TBD |
| Hazelcast Spring | 演示如何在现有的 Spring Security 应用程序中使用 Spring Session 和 Hazelcast。 HazelcastSpring 指南 | |
| HttpSession JDBC | 演示如何使用 Spring Session 用关系数据库存储替换HttpSession。 HttpSession JDBC 指南 |
|
| HttpSession JDBC XML | 演示如何使用 Spring Session 使用基于 XML 的配置将HttpSession替换为关系数据库存储。 HttpSession JDBC XML 指南 |
|
| HttpSession JDBC Spring Boot | 演示了在使用 Spring Boot 时如何使用 Spring Session 用关系数据库存储替换HttpSession。 HttpSession JDBC Spring 引导指南 |
HttpSession Integration
Spring Session 提供与HttpSession的透明集成。这意味着开发人员可以使用 Spring Session 支持的实现切换HttpSession实现。
为什么选择 Spring Session 和 HttpSession?
我们已经提到过,Spring Session 提供了与HttpSession的透明集成,但是我们可以从中获得什么好处呢?
集群会话 -Spring Session 使得支持clustered sessions变得很简单,而不必依赖于特定于应用程序容器的解决方案。
多个浏览器会话 -Spring Session 在单个浏览器实例(即类似于 Google 的多个经过身份验证的帐户)中支持Management 多个用户的会话。
RESTful APIs -Spring Session 允许在 Headers 中提供会话 ID 以与RESTful APIs一起使用
带有 Redis 的 HttpSession
通过在使用HttpSession的任何内容之前添加一个 Servlet 过滤器来启用将 Spring Session 与HttpSession一起使用。您可以使用以下任一方法来启用此功能:
Redis 基于 Java 的配置
本节介绍如何使用 Redis 通过基于 Java 的配置来备份HttpSession。
Note
HttpSession Sample提供了有关如何使用 Java 配置集成 Spring Session 和HttpSession的工作示例。您可以阅读下面的集成基本步骤,但是建议您在与自己的应用程序集成时遵循详细的 HttpSession 指南。
Spring Java 配置
添加所需的依赖关系之后,我们可以创建我们的 Spring 配置。 Spring 配置负责创建一个 Servlet 过滤器,该过滤器将HttpSession实现替换为 Spring Session 支持的实现。添加以下 Spring 配置:
@EnableRedisHttpSession (1)
public class Config {
@Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory(); (2)
}
}
- (1)
@EnableRedisHttpSessionComments 创建一个名称为springSessionRepositoryFilter的 Spring Bean,用于实现 Filter。过滤器负责替换由 Spring Session 支持的HttpSession实现。在这种情况下,Spring Session 由 Redis 支持。 - (2) 我们创建一个
RedisConnectionFactory,将 Spring Session 连接到 Redis Server。我们将连接配置为在默认端口(6379)上连接到 localhost。有关配置 Spring Data Redis 的更多信息,请参阅reference documentation。
Java Servlet 容器初始化
我们的Spring Configuration创建了一个实现Filter的名为springSessionRepositoryFilter的 Spring Bean。 springSessionRepositoryFilter bean 负责用 Spring Session 支持的自定义实现替换HttpSession。
为了让我们的Filter发挥其魔力,Spring 需要加载我们的Config类。最后,我们需要确保我们的 Servlet 容器(即 Tomcat)对每个请求都使用springSessionRepositoryFilter。幸运的是,Spring Session 提供了一个名为AbstractHttpSessionApplicationInitializer的 Util 类,这两个步骤都非常容易。您可以在下面找到一个示例:
src/main/java/sample/Initializer.java
public class Initializer extends AbstractHttpSessionApplicationInitializer { (1)
public Initializer() {
super(Config.class); (2)
}
}
Note
我们的类(Initializer)的名称无关紧要。重要的是我们扩展AbstractHttpSessionApplicationInitializer。
- (1) 第一步是扩展
AbstractHttpSessionApplicationInitializer。这可以确保为每个请求向我们的 Servlet 容器注册名称为springSessionRepositoryFilter的 Spring Bean。 - (2)
AbstractHttpSessionApplicationInitializer还提供了一种机制,可以轻松确保 Spring 加载我们的Config。
基于 Redis XML 的配置
本节介绍如何使用 Redis 通过基于 XML 的配置来备份HttpSession。
Note
HttpSession XML 示例提供了有关如何使用 XML 配置集成 Spring Session 和HttpSession的工作示例。您可以阅读下面的集成基本步骤,但是建议您在与自己的应用程序集成时遵循详细的 HttpSession XML 指南。
Spring XML 配置
添加所需的依赖关系之后,我们可以创建我们的 Spring 配置。 Spring 配置负责创建一个 Servlet 过滤器,该过滤器将HttpSession实现替换为 Spring Session 支持的实现。添加以下 Spring 配置:
src/main/webapp/WEB-INF/spring/session.xml
(1)
<context:annotation-config/>
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>
(2)
<bean class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"/>
- (1) 我们使用
<context:annotation-config/>和RedisHttpSessionConfiguration的组合,因为 Spring Session 尚未提供 XML 命名空间支持(请参阅gh-104)。这将创建一个名称为springSessionRepositoryFilter的 Spring Bean,它实现了 Filter。过滤器负责替换由 Spring Session 支持的HttpSession实现。在这种情况下,Spring Session 由 Redis 支持。 - (2) 我们创建一个
RedisConnectionFactory,将 Spring Session 连接到 Redis Server。我们将连接配置为在默认端口(6379)上连接到 localhost。有关配置 Spring Data Redis 的更多信息,请参阅reference documentation。
XML Servlet 容器初始化
我们的Spring Configuration创建了一个实现Filter的名为springSessionRepositoryFilter的 Spring Bean。 springSessionRepositoryFilter bean 负责用 Spring Session 支持的自定义实现替换HttpSession。
为了使Filter发挥其魔力,我们需要指示 Spring 加载session.xml配置。我们使用以下配置进行此操作:
src/main/webapp/WEB-INF/web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/*.xml
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
ContextLoaderListener读取 contextConfigLocation 并选择我们的 session.xml 配置。
最后,我们需要确保我们的 Servlet 容器(即 Tomcat)对每个请求都使用springSessionRepositoryFilter。以下代码段为我们执行了最后一步:
src/main/webapp/WEB-INF/web.xml
<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
DelegatingFilterProxy将查找名称为springSessionRepositoryFilter的 Bean 并将其转换为Filter。对于每个调用DelegatingFilterProxy的请求,都将调用springSessionRepositoryFilter。
具有 Pivotal GemFire 的 HttpSession
当Pivotal GemFire与 Spring Session 一起使用时,可以将 Web 应用程序的HttpSession替换为由 GemFireManagement 的“集群”实现,并可以通过 Spring Session 的 API 方便地进行访问。
使用 GemFireManagementSpring Session 的两种最常见的拓扑包括:
此外,GemFire 支持使用WAN functionality进行站点到站点的复制。配置和使用 GemFire 的 WAN 支持的能力与 Spring Session 无关,并且超出了本文档的范围。有关 GemFire WAN 功能的更多详细信息,请参见here。
GemFire Client-Server
在 Spring Session 中使用 GemFire 作为提供者时,Client-Server拓扑可能是用户更常见的配置首选项,因为与应用程序相比,GemFire 服务器将具有明显不同且独特的 JVM 堆要求。使用 Client 端-服务器拓扑使应用程序能够独立于其他应用程序进程来 Management(例如复制)应用程序状态。
在 Client 端-服务器拓扑中,使用 Spring Session 的应用程序将打开与(远程)GemFire 服务器群集的 Client 端缓存连接,以 Management 并提供对所有HttpSession状态的一致访问。
您可以使用以下任一配置 Client 端-服务器拓扑:
GemFireClient 端-服务器基于 Java 的配置
本节介绍如何使用 GemFire 的 Client 端-服务器拓扑结构来支持基于 Java 的HttpSession配置。
Note
带有 GemFire 的 HttpSession(Client 端-服务器)示例提供了有关如何使用 Java 配置集成 Spring Session 和 GemFire 以替换 HttpSession 的工作示例。您可以阅读下面的集成基本步骤,但是在与自己的应用程序集成时,建议您遵循详细的 HttpSession with GemFire(Client-Server)指南。
Spring Java 配置
添加所需的依赖项和存储库声明后,我们可以创建我们的 Spring 配置。 Spring 配置负责创建一个 Servlet 过滤器,以 Spring Session 和 GemFire 支持的实现替换HttpSession。
添加以下 Spring 配置:
@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = 30, poolName = "DEFAULT") (1)
public class ClientConfig {
static final long DEFAULT_WAIT_DURATION = TimeUnit.SECONDS.toMillis(20);
static final CountDownLatch latch = new CountDownLatch(1);
static final String DEFAULT_GEMFIRE_LOG_LEVEL = "warning";
@Bean
static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
Properties gemfireProperties() { (2)
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", applicationName());
gemfireProperties.setProperty("log-level", logLevel());
return gemfireProperties;
}
String applicationName() {
return "samples:httpsession-gemfire-clientserver:"
.concat(getClass().getSimpleName());
}
String logLevel() {
return System.getProperty("sample.httpsession.gemfire.log-level",
DEFAULT_GEMFIRE_LOG_LEVEL);
}
@Bean
ClientCacheFactoryBean gemfireCache(
@Value("${spring.session.data.gemfire.port:" + ServerConfig.SERVER_PORT + "}") int port) { (3)
ClientCacheFactoryBean clientCacheFactory = new ClientCacheFactoryBean();
clientCacheFactory.setClose(true);
clientCacheFactory.setProperties(gemfireProperties());
// GemFire Pool settings (4)
clientCacheFactory.setKeepAlive(false);
clientCacheFactory.setPingInterval(TimeUnit.SECONDS.toMillis(5));
clientCacheFactory.setReadTimeout(2000); // 2 seconds
clientCacheFactory.setRetryAttempts(1);
clientCacheFactory.setSubscriptionEnabled(true);
clientCacheFactory.setThreadLocalConnections(false);
clientCacheFactory.setServers(Collections.singletonList(
newConnectionEndpoint(ServerConfig.SERVER_HOST, port)));
return clientCacheFactory;
}
ConnectionEndpoint newConnectionEndpoint(String host, int port) {
return new ConnectionEndpoint(host, port);
}
@Bean
BeanPostProcessor gemfireCacheServerReadyBeanPostProcessor() { (5)
return new BeanPostProcessor() {
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if ("gemfirePool".equals(beanName)) {
ClientMembership.registerClientMembershipListener(
new ClientMembershipListenerAdapter() {
@Override
public void memberJoined(ClientMembershipEvent event) {
latch.countDown();
}
});
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof Pool && "gemfirePool".equals(beanName)) {
try {
Assert.state(latch.await(DEFAULT_WAIT_DURATION, TimeUnit.MILLISECONDS),
String.format("GemFire Cache Server failed to start on host [%1$s] and port [%2$d]",
ServerConfig.SERVER_HOST, ServerConfig.SERVER_PORT));
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
return bean;
}
};
}
- (1)
@EnableGemFireHttpSessionComments 创建一个实现Filter的名为springSessionRepositoryFilter的 Spring bean。过滤器是由 Spring Session 和 GemFire 支持的实现替换HttpSession的。 - (2) 接下来,我们注册一个
Propertiesbean,该 bean 使我们能够使用GemFire 的系统属性配置 GemFireClient 端缓存的某些方面。 - (3) 我们使用
Properties来配置 GemFireClientCache的实例。 - (4) 然后,我们在 Client 端/服务器拓扑中配置
PoolClient 端连接以与 GemFire 服务器对话。在我们的配置中,我们对超时,连接数等使用了合理的设置。同样,已将Pool配置为直接连接到服务器。从PoolFactory API了解有关各种Pool配置设置的更多信息。 - (5) 最后,我们包含一个 Spring
BeanPostProcessor来阻止 Client 端,直到我们的 GemFire 服务器启动并运行,侦听并接受 Client 端连接为止。
gemfireCacheServerReadyBeanPostProcessor是必需的,以便在测试过程中以自动化的方式协调 Client 端和服务器,但是在 GemFire 集群目前已经运行的情况下(例如在 Producing)则不需要。
BeanPostProcessor使用 GemFire ClientMembershipListener,当 Client 端成功连接到服务器时,该通知会通知。构建连接后,侦听器将释放postProcessAfterInitialization回调中BeanPostProcessorawait 的闩锁(直到指定的超时)以阻止 Client 端。
Tip
在群集可能包含数百个 GemFire 数据节点(服务器)的典型 GemFire 部署中,Client 端连接到群集中运行的一个或多个 GemFire Locator 更为常见。定位器将有关可用服务器,负载以及哪些服务器具有感兴趣的 Client 端数据的元数据传递给 Client 端,这对于单跳直接数据访问特别重要。查看有关GemFire 用户指南中的 Client 端/服务器拓扑的更多详细信息。
Note
有关配置* Spring Data GemFire *的更多信息,请参阅reference guide。
@EnableGemFireHttpSessionComments 使开发人员可以使用以下属性立即配置 Spring Session 和 GemFire 的某些方面:
maxInactiveIntervalInSeconds-控制* HttpSession *空闲超时时间(默认为 30 分钟 )。regionName-指定用于存储HttpSession状态的 GemFire 区域的名称(默认值为“ ClusteredSpringSessions ”)。clientRegionShort-用 GemFire ClientRegionShortcut指定 GemFire 的数据 ManagementPolicy(默认值为PROXY)。此属性仅在配置 Client 端区域时使用。poolName-用于将 Client 端连接到服务器集群的专用 GemFire 池的名称。该属性仅在应用程序是 GemFire 缓存 Client 端时使用。默认为gemfirePool。serverRegionShort-使用 GemFire RegionShortcut(默认值为PARTITION)指定 GemFire 的数据 ManagementPolicy。仅在配置服务器区域或使用 p2p 拓扑时才使用此属性。
Note
请务必注意,如果 Client 端区域是PROXY或CACHING_PROXY,则 GemFireClient 端区域名称必须与服务器区域名称相同。如果用于存储 Spring 会话的 Client 端区域名称为LOCAL,则名称不需要匹配,但是请记住,您的会话状态不会传播到服务器,并且您失去了使用 GemFire 来存储和 Management 分布式复制会话的所有好处集群中的状态信息。
Note
serverRegionShort在 Client 端/服务器缓存配置中将被忽略,并且仅在对等(P2P)拓扑(更具体地说,使用 GemFire 对等缓存)时适用。
Server Configuration
我们仅涵盖了等式的一侧。我们还需要一个 GemFire 服务器,以便我们的 Client 端与之对话并向服务器发送会话状态进行 Management。
在此示例中,我们将使用以下 GemFire Server Java 配置:
@EnableGemFireHttpSession(maxInactiveIntervalInSeconds = 30) (1)
public class ServerConfig {
static final int SERVER_PORT = 12480;
static final String DEFAULT_GEMFIRE_LOG_LEVEL = "warning";
static final String SERVER_HOST = "localhost";
@SuppressWarnings("resource")
public static void main(String[] args) throws IOException { (5)
new AnnotationConfigApplicationContext(ServerConfig.class)
.registerShutdownHook();
}
@Bean
static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
Properties gemfireProperties() { (2)
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", applicationName());
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-level", logLevel());
gemfireProperties.setProperty("jmx-manager", "true");
gemfireProperties.setProperty("jmx-manager-start", "true");
return gemfireProperties;
}
String applicationName() {
return "samples:httpsession-gemfire-clientserver:"
.concat(getClass().getSimpleName());
}
String logLevel() {
return System.getProperty("sample.httpsession.gemfire.log-level",
DEFAULT_GEMFIRE_LOG_LEVEL);
}
@Bean
CacheFactoryBean gemfireCache() { (3)
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setProperties(gemfireProperties());
return gemfireCache;
}
@Bean
CacheServerFactoryBean gemfireCacheServer(Cache gemfireCache,
@Value("${spring.session.data.gemfire.port:" + SERVER_PORT + "}") int port) { (4)
CacheServerFactoryBean gemfireCacheServer = new CacheServerFactoryBean();
gemfireCacheServer.setAutoStartup(true);
gemfireCacheServer.setBindAddress(SERVER_HOST);
gemfireCacheServer.setCache(gemfireCache);
gemfireCacheServer.setHostNameForClients(SERVER_HOST);
gemfireCacheServer.setMaxTimeBetweenPings(Long.valueOf(TimeUnit.SECONDS.toMillis(60)).intValue());
gemfireCacheServer.setPort(port);
return gemfireCacheServer;
}
}
- (1) 在服务器上,我们还使用
@EnableGemFireHttpSessionComments 配置 Spring Session。这样可以确保 Client 端和服务器上的 Region 名称都匹配(在此示例中,我们使用默认的“ * ClusteredSpringSessions *”)。我们还将会话超时设置为 30 秒 。稍后,我们将看到如何使用此超时。 - (2) 接下来,我们使用 GemFire 系统属性配置 GemFire 服务器,就像我们的 P2P 示例一样。在
mcast-port设置为 0 且未指定locators属性的情况下,我们的服务器将是独立的。我们还允许 JMXClient 端(例如* Gfsh *)通过使用特定于 GemFire 的 JMX 系统属性连接到我们的服务器。 - (3) 然后,我们创建一个使用 GemFire 系统属性初始化的 GemFire 对等体
Cache的实例。 - (4) 我们还设置了一个在 localhost 上运行的 GemFire
CacheServer实例,监听端口 12480 ,准备接受我们的 Client 端连接。 - (5) 最后,我们声明一个
main方法作为从命令行启动和运行 GemFire 服务器的入口点。
Java Servlet 容器初始化
我们的Spring Java 配置创建了一个实现Filter的名为springSessionRepositoryFilter的 Spring bean。 springSessionRepositoryFilter bean 负责用 Spring Session 和 GemFire 支持的自定义实现替换HttpSession。
为了让我们的Filter发挥其魔力,Spring 需要加载我们的ClientConfig类。我们还需要确保我们的 Servlet 容器(即 Tomcat)对每个请求都使用springSessionRepositoryFilter。幸运的是,Spring Session 提供了一个名为AbstractHttpSessionApplicationInitializer的 Util 类,使这两个步骤都非常容易。
您可以在下面找到一个示例:
src/main/java/sample/Initializer.java
public class Initializer extends AbstractHttpSessionApplicationInitializer { (1)
public Initializer() {
super(ClientConfig.class); (2)
}
}
Note
我们类的名称(Initializer)无关紧要。重要的是我们扩展AbstractHttpSessionApplicationInitializer。
- (1) 第一步是扩展
AbstractHttpSessionApplicationInitializer。这确保了名为springSessionRepositoryFilter的 Spring bean 已在我们的 Servlet 容器中注册并用于每个请求。 - (2)
AbstractHttpSessionApplicationInitializer还提供了一种机制,可以轻松地让 Spring 加载ClientConfig。
GemFireClient 端-服务器基于 XML 的配置
本节介绍如何使用 GemFire 的 Client 端-服务器拓扑结构支持基于 XML 的HttpSession配置。
Note
使用 XML 示例的 GemFire(Client 端-服务器)上的 HttpSession提供了一个工作示例,说明如何使用 XML 配置集成 Spring Session 和 GemFire 以替换HttpSession。您可以阅读下面的集成基本步骤,但在与自己的应用程序集成时,建议您使用 XML 指南以及带有 GemFire(Client 端-服务器)的详细 HttpSession 和 XML 指南。
Spring XML 配置
添加所需的依赖项和存储库声明后,我们可以创建我们的 Spring 配置。 Spring 配置负责创建一个 Servlet 过滤器,以 Spring Session 和 GemFire 支持的实现替换HttpSession。
添加以下 Spring 配置:
(1)
<context:annotation-config/>
(2)
<context:property-placeholder location="classpath:META-INF/spring/application.properties"/>
(3)
<bean class="sample.GemFireCacheServerReadyBeanPostProcessor"/>
(4)
<util:properties id="gemfireProperties">
<prop key="log-level">${sample.httpsession.gemfire.log-level:warning}</prop>
</util:properties>
(5)
<gfe:client-cache properties-ref="gemfireProperties" pool-name="gemfirePool"/>
(6)
<gfe:pool keep-alive="false"
ping-interval="5000"
read-timeout="5000"
retry-attempts="1"
subscription-enabled="true"
thread-local-connections="false">
<gfe:server host="${application.gemfire.client-server.host}"
port="${spring.session.data.gemfire.port:${application.gemfire.client-server.port}}"/>
</gfe:pool>
(7)
<bean class="org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration"
p:maxInactiveIntervalInSeconds="30" p:poolName="DEFAULT"/>
- (1) 启用了带有
<context:annotation-config/>元素的 SpringComments 配置支持,因此将对 XML 配置中声明的,使用 Spring 或 Spring 支持的标准 JavaComments 进行 Comments 的任何 Spring bean 进行适当配置。 - (2)
META-INF/spring/application.properties文件与PropertySourcesPlaceholderConfigurerbean 一起使用,以使用适当的属性值替换 Spring XML 配置元数据中的占位符。 - (3) 然后注册了“ GemFireCacheSeverReadyBeanPostProcessor”,以确定指定主机/端口上的 GemFire 服务器是否正在运行并监听 Client 端连接,从而阻止 Client 端启动,直到服务器可用并就绪为止。
- (4) 接下来,我们包含一个
Propertiesbean,以使用GemFire 的系统属性配置 GemFireClient 端缓存的某些方面。在这种情况下,我们只是从应用程序特定的 System 属性设置 GemFire 的log-level,如果未指定则默认为warning。 - (5) 然后,我们创建一个 GemFire
ClientCache实例,并以gemfireProperties初始化。 - (6) 我们配置了一个 Client 端连接池,以在 Client 端/服务器拓扑中与 GemFire 服务器对话。在我们的配置中,我们对超时,连接数等使用了合理的设置。另外,我们的
Pool已配置为直接连接到服务器。 - (7) 最后,
GemFireHttpSessionConfiguration已注册以启用 Spring Session 功能。
Tip
在群集可能包含数百个 GemFire 数据节点(服务器)的典型 GemFire 部署中,Client 端连接到群集中运行的一个或多个 GemFire Locator 更为常见。定位器将有关可用服务器,负载以及哪些服务器具有感兴趣的 Client 端数据的元数据传递给 Client 端,这对于单跳直接数据访问特别重要。查看有关GemFire 用户指南中的 Client 端/服务器拓扑的更多详细信息。
Note
有关配置* Spring Data GemFire *的更多信息,请参阅reference guide。
Server Configuration
我们仅涵盖了等式的一侧。我们还需要一个 GemFire 服务器,以便我们的 Client 端与之对话并向服务器发送会话状态信息以进行 Management。
在此示例中,我们将使用以下 GemFire Server Java 配置:
(1)
<context:annotation-config/>
(2)
<context:property-placeholder location="classpath:META-INF/spring/application.properties"/>
(3)
<util:properties id="gemfireProperties">
<prop key="name">GemFireClientServerHttpSessionXmlSample</prop>
<prop key="mcast-port">0</prop>
<prop key="log-level">${sample.httpsession.gemfire.log-level:warning}</prop>
<prop key="jmx-manager">true</prop>
<prop key="jmx-manager-start">true</prop>
</util:properties>
(4)
<gfe:cache properties-ref="gemfireProperties"/>
(5)
<gfe:cache-server auto-startup="true"
bind-address="${application.gemfire.client-server.host}"
host-name-for-clients="${application.gemfire.client-server.host}"
port="${spring.session.data.gemfire.port:${application.gemfire.client-server.port}}"/>
(6)
<bean class="org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration"
p:maxInactiveIntervalInSeconds="30"/>
- (1) 首先,我们使用
<context:annotation-config>元素启用 SpringComments 配置支持,以便在 XML 配置中声明的,使用 Spring 或 Spring 支持的 Spring 或 Standard JavaComments 进行 Comments 的任何 Spring bean 都将得到适当配置。 - (2) 已注册
PropertySourcesPlaceholderConfigurer,以将我们的 Spring XML 配置元数据中的占位符替换为META-INF/spring/application.properties文件中的属性值。 - (3) 接下来,我们使用 GemFire 系统属性配置 GemFire 服务器,就像我们的 P2P 示例一样。在
mcast-port设置为 0 且未指定locators属性的情况下,我们的服务器将是独立的。我们还允许 JMXClient 端(例如* Gfsh *)通过使用特定于 GemFire 的 JMX 系统属性连接到我们的服务器。 - (4) 然后,我们创建一个 GemFire 对等体
Cache的实例,该实例使用 GemFire 系统属性进行了初始化。 - (5) 我们还设置了一个在 localhost 上运行的 GemFire
CacheServer实例,监听端口 11235 ,准备接受我们的 Client 端连接。 - (6) 最后,我们通过注册
GemFireHttpSessionConfiguration实例来启用与 Client 端相同的 Spring Session 功能,除了我们将会话过期超时设置为 30 秒 。稍后我们将解释这意味着什么。
GemFire 服务器配置通过以下方式进行引导:
@Configuration (1)
@ImportResource("META-INF/spring/session-server.xml") (2)
public class Application {
public static void main(final String[] args) {
new AnnotationConfigApplicationContext(Application.class)
.registerShutdownHook();
}
}
Tip
除了使用 main 方法创建简单的 Java 类之外,还可以使用* Spring Boot *。
- (1)
@ConfigurationComments 使用 Spring 的 Comments 配置支持将此 Java 类指定为 Spring 配置元数据的源。 - (2) 最初,配置来自
META-INF/spring/session-server.xml文件,这也是此示例中未使用* Spring Boot *的原因,因为使用 XML 似乎无法使用 Spring Boot 的目的和优点。但是,此 samples 是关于演示如何使用 Spring XML 来配置 GemFireClient 端和服务器的。
XML Servlet 容器初始化
我们的Spring XML 配置创建了一个实现Filter的名为springSessionRepositoryFilter的 Spring bean。 springSessionRepositoryFilter bean 负责用由 Spring Session 和 GemFire 支持的自定义实现替换HttpSession。
为了使Filter发挥其魔力,我们需要指示 Spring 加载session-client.xml配置文件。我们使用以下配置进行此操作:
src/main/webapp/WEB-INF/web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/session-client.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
ContextLoaderListener读取contextConfigLocation上下文参数值,并获取我们的* session-client.xml *配置文件。
最后,我们需要确保我们的 Servlet 容器(即 Tomcat)对每个请求都使用springSessionRepositoryFilter。
以下代码段为我们执行了最后一步:
src/main/webapp/WEB-INF/web.xml
<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
DelegatingFilterProxy将查找名称为springSessionRepositoryFilter的 bean,并将其转换为Filter。对于每个调用DelegatingFilterProxy的请求,都将调用springSessionRepositoryFilter。
GemFire 对等(P2P)
也许不太常见的是使用Peer-To-Peer (P2P)拓扑将 Spring Session 应用程序配置为 GemFire 集群中的对等成员。在这种配置中,Spring Session 应用程序将是 GemFire 集群中的实际数据节点(服务器),并且不是以前的缓存 Client 端。
这种方法的一个优点是应用程序接近应用程序的状态(即它的数据)。但是,还有其他有效的方法可以完成类似的数据相关计算,例如使用 GemFire 的Function Execution。当 GemFire 充当 Spring Session 中的提供者时,可以使用 GemFire 的其他features中的任何一个。
P2P 对于测试目的以及更小,更集中且自包含的应用程序(例如微服务体系结构中的应用程序)都非常有用,并且肯定会改善应用程序的延迟,吞吐量和一致性需求。
您可以使用以下任一配置对等(P2P)拓扑:
基于 Java 的 GemFire 对等(P2P)配置
本节介绍如何使用 GemFire 的对等(P2P)拓扑来使用基于 Java 的配置来支持HttpSession。
Note
带有 GemFire 的 HttpSession(P2P)示例提供了有关如何使用 Java 配置集成 Spring Session 和 GemFire 以替换HttpSession的工作示例。您可以阅读下面的集成基本步骤,但是在与自己的应用程序集成时,建议您遵循详细的 HttpSession with GemFire(P2P)指南。
Spring Java 配置
添加所需的依赖项和存储库声明后,我们可以创建我们的 Spring 配置。 Spring 配置负责创建一个 Servlet 过滤器,以 Spring Session 和 GemFire 支持的实现替换HttpSession。
添加以下 Spring 配置:
@EnableGemFireHttpSession (1)
public class Config {
@Bean
Properties gemfireProperties() { (2)
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", "GemFireP2PHttpSessionSample");
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-level",
System.getProperty("sample.httpsession.gemfire.log-level", "warning"));
gemfireProperties.setProperty("jmx-manager", "true");
gemfireProperties.setProperty("jmx-manager-start", "true");
return gemfireProperties;
}
@Bean
CacheFactoryBean gemfireCache() { (3)
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setProperties(gemfireProperties());
return gemfireCache;
}
}
- (1)
@EnableGemFireHttpSessionComments 创建一个实现Filter的名为springSessionRepositoryFilter的 Spring bean。过滤器将HttpSession替换为 Spring Session 支持的实现。在这种情况下,Spring Session 由 GemFire 支持。 - (2) 然后,我们使用标准 GemFire 系统属性配置 GemFire 对等缓存。我们使用
name属性为 GemFire 数据节点命名,并将mcast-port设置为 0.如果没有locators属性,则该数据节点将是独立服务器。 GemFire 的log-level是使用特定于应用程序的系统属性(sample.httpsession.gemfire.log-level)设置的,用户可以在使用 Maven 或 Gradle 运行此示例应用程序时在命令行上指定该属性(默认值为“ * warning *”)。 - (3) 最后,我们创建 GemFire 对等缓存的实例,该实例将 GemFire 嵌入与正在运行的 Spring Session 示例应用程序相同的 JVM 进程中。
Tip
此外,我们还使用特定于 GemFire 的 JMX 系统属性将该数据节点(服务器)配置为 GemFireManagement 器,该属性使 JMXClient 端(例如* Gfsh *)能够连接到此运行的数据节点。
Note
有关配置* Spring Data GemFire *的更多信息,请参阅reference guide。
@EnableGemFireHttpSessionComments 使开发人员可以使用以下属性立即配置 Spring Session 和 GemFire 的某些方面:
maxInactiveIntervalInSeconds-控制 HttpSession 空闲超时时间(默认为 30 分钟 )。regionName-指定用于存储HttpSession状态的 GemFire 区域的名称(默认值为“ * ClusteredSpringSessions *”)。serverRegionShort-用 GemFire RegionShortcut指定 GemFire 数据 ManagementPolicy(默认值为PARTITION)。
Note
clientRegionShort在对等缓存配置中将被忽略,并且仅在 Client 端-服务器拓扑(更具体地说,是使用 GemFireClient 端缓存)时适用。
Java Servlet 容器初始化
我们的Spring Java 配置创建了一个实现Filter的名为springSessionRepositoryFilter的 Spring bean。 springSessionRepositoryFilter bean 负责用 Spring Session 和 GemFire 支持的自定义实现替换HttpSession。
为了让我们的Filter发挥其魔力,Spring 需要加载我们的Config类。我们还需要确保我们的 Servlet 容器(即 Tomcat)对每个请求都使用springSessionRepositoryFilter。幸运的是,Spring Session 提供了一个名为AbstractHttpSessionApplicationInitializer的 Util 类,使这两个步骤都非常容易。
您可以在下面找到一个示例:
src/main/java/sample/Initializer.java
public class Initializer extends AbstractHttpSessionApplicationInitializer { (1)
public Initializer() {
super(Config.class); (2)
}
}
Note
我们类的名称(Initializer)无关紧要。重要的是我们扩展AbstractHttpSessionApplicationInitializer。
- (1) 第一步是扩展
AbstractHttpSessionApplicationInitializer。这确保了名为springSessionRepositoryFilter的 Spring bean 已在我们的 Servlet 容器中注册并用于每个请求。 - (2)
AbstractHttpSessionApplicationInitializer还提供了一种机制,可以轻松地让 Spring 加载Config。
GemFire 对等(P2P)基于 XML 的配置
本节介绍如何使用 GemFire 的对等(P2P)拓扑来使用基于 XML 的配置来支持HttpSession。
Note
使用 XML 示例的带有 GemFire(P2P)的 HttpSession提供了一个工作示例,说明如何使用 XML 配置集成 Spring Session 和 GemFire 以替换HttpSession。您可以阅读下面的集成基本步骤,但是在与自己的应用程序集成时,建议您使用 XML 指南以及带有 GemFire(P2P)的详细 HttpSession 进行遵循。
Spring XML 配置
添加所需的依赖项和存储库声明后,我们可以创建我们的 Spring 配置。 Spring 配置负责创建一个 Servlet 过滤器,以 Spring Session 和 GemFire 支持的实现替换HttpSession。
添加以下 Spring 配置:
src/main/webapp/WEB-INF/spring/session.xml
(1)
<context:annotation-config/>
<context:property-placeholder/>
<bean class="org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration"/>
(2)
<util:properties id="gemfireProperties">
<prop key="name">GemFireP2PHttpSessionXmlSample</prop>
<prop key="mcast-port">0</prop>
<prop key="log-level">${sample.httpsession.gemfire.log-level:warning}</prop>
<prop key="jmx-manager">true</prop>
<prop key="jmx-manager-start">true</prop>
</util:properties>
(3)
<gfe:cache properties-ref="gemfireProperties" use-bean-factory-locator="false"/>
- (1) 我们使用
<context:annotation-config/>和GemFireHttpSessionConfiguration的组合,因为 Spring Session 尚未提供 XML 命名空间支持(请参阅gh-104)。这将创建一个名称为springSessionRepositoryFilter的 Spring Bean,该 Bean 实现Filter。过滤器是用 Spring Session 支持的实现替换HttpSession的工具。在这种情况下,Spring Session 由 GemFire 支持。 - (2) 然后,我们使用标准 GemFire 系统属性配置 GemFire 对等缓存。我们使用
name属性为 GemFire 数据节点命名,并将mcast-port设置为 0.如果没有locators属性,则该数据节点将是独立服务器。 GemFire 的log-level是使用特定于应用程序的系统属性(sample.httpsession.gemfire.log-level)设置的,用户可以在使用 Maven 或 Gradle 运行此应用程序时在命令行上指定该属性(默认值为“ * warning *”)。 - (3) 最后,我们创建 GemFire 对等缓存的实例,该实例将 GemFire 嵌入与正在运行的 Spring Session 示例应用程序相同的 JVM 进程中。
Tip
此外,我们还使用特定于 GemFire 的 JMX 系统属性将该数据节点(服务器)配置为 GemFireManagement 器,该属性使 JMXClient 端(例如* Gfsh *)能够连接到此运行的数据节点。
Note
有关配置* Spring Data GemFire *的更多信息,请参阅reference guide。
XML Servlet 容器初始化
我们的Spring XML 配置创建了一个实现Filter的名为springSessionRepositoryFilter的 Spring bean。 springSessionRepositoryFilter bean 负责用由 Spring Session 和 GemFire 支持的自定义实现替换HttpSession。
为了使Filter发挥神奇的作用,我们需要指示 Spring 加载session.xml配置文件。我们使用以下配置进行此操作:
src/main/webapp/WEB-INF/web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/*.xml
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
ContextLoaderListener读取contextConfigLocation上下文参数值,并获取我们的* session.xml *配置文件。
最后,我们需要确保我们的 Servlet 容器(即 Tomcat)对每个请求都使用springSessionRepositoryFilter。
以下代码段为我们执行了最后一步:
src/main/webapp/WEB-INF/web.xml
<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
DelegatingFilterProxy将查找名称为springSessionRepositoryFilter的 bean,并将其转换为Filter。对于每个调用DelegatingFilterProxy的请求,都将调用springSessionRepositoryFilter。
具有 JDBC 的 HttpSession
通过在使用HttpSession的任何内容之前添加一个 Servlet 过滤器来启用将 Spring Session 与HttpSession一起使用。您可以使用以下任一方法来启用此功能:
基于 JDBC Java 的配置
本节描述如何使用关系数据库通过基于 Java 的配置来支持HttpSession。
Note
HttpSession JDBC 示例提供了有关如何使用 Java 配置集成 Spring Session 和HttpSession的工作示例。您可以阅读下面的集成基本步骤,但是建议您在与自己的应用程序集成时遵循详细的 HttpSession JDBC 指南。
Spring Java 配置
添加所需的依赖关系后,我们可以创建我们的 Spring 配置。 Spring 配置负责创建一个 Servlet 过滤器,该过滤器将HttpSession实现替换为 Spring Session 支持的实现。添加以下 Spring 配置:
@EnableJdbcHttpSession (1)
public class Config {
@Bean
public EmbeddedDatabase dataSource() {
return new EmbeddedDatabaseBuilder() (2)
.setType(EmbeddedDatabaseType.H2)
.addScript("org/springframework/session/jdbc/schema-h2.sql").build();
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource); (3)
}
}
- (1)
@EnableJdbcHttpSessionComments 创建一个名称为springSessionRepositoryFilter的 Spring Bean,该 Bean 实现了 Filter。过滤器负责替换由 Spring Session 支持的HttpSession实现。在这种情况下,Spring Session 由关系数据库支持。 - (2) 我们创建一个
dataSource,将 Spring Session 连接到 H2 数据库的嵌入式实例。我们将 H2 数据库配置为使用 Spring Session 中包含的 SQL 脚本创建数据库表。 - (3) 我们创建一个
transactionManager来 Management 先前配置的dataSource的 Transaction。
有关如何配置与数据访问相关的问题的更多信息,请参阅Spring 框架参考文档。
Java Servlet 容器初始化
我们的Spring Configuration创建了一个名为springSessionRepositoryFilter的 Spring Bean,它实现了Filter。 springSessionRepositoryFilter bean 负责用 Spring Session 支持的自定义实现替换HttpSession。
为了让我们的Filter发挥其魔力,Spring 需要加载我们的Config类。最后,我们需要确保我们的 Servlet 容器(即 Tomcat)对每个请求都使用springSessionRepositoryFilter。幸运的是,Spring Session 提供了一个名为AbstractHttpSessionApplicationInitializer的 Util 类,这两个步骤都非常容易。您可以在下面找到一个示例:
src/main/java/sample/Initializer.java
public class Initializer extends AbstractHttpSessionApplicationInitializer { (1)
public Initializer() {
super(Config.class); (2)
}
}
Note
我们的类(Initializer)的名称无关紧要。重要的是我们扩展AbstractHttpSessionApplicationInitializer。
- (1) 第一步是扩展
AbstractHttpSessionApplicationInitializer。这可以确保为每个请求向我们的 Servlet 容器注册名称为springSessionRepositoryFilter的 Spring Bean。 - (2)
AbstractHttpSessionApplicationInitializer还提供了一种机制,可以轻松确保 Spring 加载我们的Config。
基于 JDBC XML 的配置
本节描述如何使用关系数据库通过基于 XML 的配置来支持HttpSession。
Note
HttpSession JDBC XML 示例提供了有关如何使用 XML 配置集成 Spring Session 和HttpSession的工作示例。您可以阅读下面的集成基本步骤,但是在与自己的应用程序集成时,建议您遵循详细的 HttpSession JDBC XML 指南。
Spring XML 配置
添加所需的依赖关系之后,我们可以创建我们的 Spring 配置。 Spring 配置负责创建一个 Servlet 过滤器,该过滤器将HttpSession实现替换为 Spring Session 支持的实现。添加以下 Spring 配置:
src/main/webapp/WEB-INF/spring/session.xml
(1)
<context:annotation-config/>
<bean class="org.springframework.session.jdbc.config.annotation.web.http.JdbcHttpSessionConfiguration"/>
(2)
<jdbc:embedded-database id="dataSource" database-name="testdb" type="H2">
<jdbc:script location="classpath:org/springframework/session/jdbc/schema-h2.sql"/>
</jdbc:embedded-database>
(3)
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource"/>
</bean>
- (1) 我们使用
<context:annotation-config/>和JdbcHttpSessionConfiguration的组合,因为 Spring Session 尚未提供 XML 命名空间支持(请参阅gh-104)。这将创建一个名称为springSessionRepositoryFilter的 Spring Bean,它实现了 Filter。过滤器负责替换由 Spring Session 支持的HttpSession实现。在这种情况下,Spring Session 由关系数据库支持。 - (2) 我们创建一个
dataSource,该dataSource将 Spring Session 连接到 H2 数据库的嵌入式实例。我们将 H2 数据库配置为使用 Spring Session 中包含的 SQL 脚本创建数据库表。 - (3) 我们创建一个
transactionManager来 Management 先前配置的dataSource的 Transaction。
有关如何配置与数据访问相关的问题的更多信息,请参阅Spring 框架参考文档。
XML Servlet 容器初始化
我们的Spring Configuration创建了一个实现Filter的名为springSessionRepositoryFilter的 Spring Bean。 springSessionRepositoryFilter bean 负责用 Spring Session 支持的自定义实现替换HttpSession。
为了使Filter发挥其魔力,我们需要指示 Spring 加载session.xml配置。我们使用以下配置进行此操作:
src/main/webapp/WEB-INF/web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/*.xml
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
ContextLoaderListener读取 contextConfigLocation 并选择我们的 session.xml 配置。
最后,我们需要确保我们的 Servlet 容器(即 Tomcat)对每个请求都使用springSessionRepositoryFilter。以下代码段为我们执行了最后一步:
src/main/webapp/WEB-INF/web.xml
<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
DelegatingFilterProxy将查找名称为springSessionRepositoryFilter的 Bean 并将其转换为Filter。对于每个调用DelegatingFilterProxy的请求,都将调用springSessionRepositoryFilter。
基于 JDBC Spring Boot 的配置
本节描述了使用 Spring Boot 时如何使用关系数据库来备份HttpSession。
Note
HttpSession JDBC Spring Boot 示例提供了有关如何使用 Spring Boot 集成 Spring Session 和HttpSession的工作示例。您可以阅读下面的集成基本步骤,但是在与自己的应用程序集成时,建议您遵循详细的 HttpSession JDBC Spring Boot Guide。
Spring Configuration
添加所需的依赖关系之后,我们可以创建我们的 Spring 配置。 Spring 配置负责创建一个 Servlet 过滤器,该过滤器将HttpSession实现替换为 Spring Session 支持的实现。添加以下 Spring 配置:
@EnableJdbcHttpSession (1)
public class HttpSessionConfig {
}
- (1)
@EnableJdbcHttpSessionComments 创建一个名称为springSessionRepositoryFilter的 Spring Bean,用于实现 Filter。过滤器负责替换由 Spring Session 支持的HttpSession实现。在这种情况下,Spring Session 由关系数据库支持。
配置数据源
Spring Boot 自动创建一个DataSource来将 Spring Session 连接到 H2 数据库的嵌入式实例。在生产环境中,您需要确保更新配置以指向关系数据库。例如,您可以在 application.properties 中包括以下内容
src/main/resources/application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/myapp
spring.datasource.username=myapp
spring.datasource.password=secret
有关更多信息,请参阅 Spring Boot 文档的配置数据源部分。
Servlet 容器初始化
我们的Spring Configuration创建了一个实现Filter的名为springSessionRepositoryFilter的 Spring Bean。 springSessionRepositoryFilter bean 负责用 Spring Session 支持的自定义实现替换HttpSession。
为了让我们的Filter发挥其魔力,Spring 需要加载我们的Config类。最后,我们需要确保我们的 Servlet 容器(即 Tomcat)对每个请求都使用springSessionRepositoryFilter。幸运的是,Spring Boot 为我们完成了这两个步骤。
与 Mongo 进行 HttpSession
通过在使用HttpSession的任何内容之前添加一个 Servlet 过滤器来启用将 Spring Session 与HttpSession一起使用。
本节介绍如何使用 Mongo 通过基于 Java 的配置来支持HttpSession。
Note
HttpSession Mongo 示例提供了有关如何使用 Java 配置集成 Spring Session 和HttpSession的工作示例。您可以阅读下面的集成基本步骤,但是建议您在与自己的应用程序集成时遵循详细的 HttpSession 指南。
您所要做的就是添加以下 Spring Configuration:
@EnableMongoHttpSession (1)
public class HttpSessionConfig {
@Bean
public JdkMongoSessionConverter jdkMongoSessionConverter() {
return new JdkMongoSessionConverter(); (2)
}
}
- (1)
@EnableMongoHttpSessionComments 创建一个名称为springSessionRepositoryFilter的 Spring Bean,用于实现 Filter。过滤器负责替换由 Spring Session 支持的HttpSession实现。在这种情况下,Spring Session 由 Mongo 支持。 - (2) 我们明确配置
JdkMongoSessionConverter,因为无法使用 Jackson 来自动持久化 Spring Security 的对象(如果 Jackson 在 Classpath 中,则为默认值)。
会话序列化机制
为了能够将会话对象持久存储在 MongoDB 中,我们需要提供序列化/反序列化机制。根据您的 Classpath,Spring Session 将选择以下两个内置转换器之一:
JacksonMongoSessionConverterObjectMapper班有空,或者JdkMongoSessionConverter否则。
JacksonMongoSessionConverter
该机制使用 Jackson 来将会话对象与 JSON 序列化。当在 Classpath 中检测到 Jackson 且用户未明确注册AbstractMongoSessionConverter Bean 时,JacksonMongoSessionConverter将是默认值。
如果您想提供自定义的 Jackson 模块,可以通过显式注册JacksonMongoSessionConverter来完成:
@Configuration
@EnableMongoHttpSession
public class MongoJacksonSessionConfiguration {
@Bean
public AbstractMongoSessionConverter mongoSessionConverter() {
return new JacksonMongoSessionConverter(getJacksonModules());
}
public Iterable<Module> getJacksonModules() {
return Collections.<Module>singletonList(new MyJacksonModule());
}
}
JdkMongoSessionConverter
JdkMongoSessionConverter使用标准 Java 序列化将会话属性 Map 以二进制形式持久化到 MongoDB。但是,标准会话元素(例如 id,访问时间等)仍被编写为普通的 Mongo 对象,并且无需额外的努力即可读取和查询。如果 Jackson 不在 Classpath 上并且未定义显式AbstractMongoSessionConverter Bean,则使用JdkMongoSessionConverter。您可以通过将JdkMongoSessionConverter定义为 Bean 来显式注册。
@Configuration
@EnableMongoHttpSession
public class MongoJdkSessionConfiguration {
@Bean
public AbstractMongoSessionConverter mongoSessionConverter() {
return new JdkMongoSessionConverter();
}
}
还有一个带有Serializer和Deserializer对象的构造函数,允许您传递自定义实现,当您要使用非默认类加载器时,这一点尤其重要。
使用自定义转换器
您可以通过扩展AbstractMongoSessionConverter类来创建自己的会话转换器。该实现将用于序列化,反序列化对象以及提供查询以访问会话。
带有 Hazelcast 的 HttpSession
通过在使用HttpSession的任何内容之前添加一个 Servlet 过滤器来启用将 Spring Session 与HttpSession一起使用。
本节介绍如何使用 Hazelcast 通过基于 Java 的配置来备份HttpSession。
Note
榛果 Springsample提供了有关如何使用 Java 配置集成 Spring Session 和HttpSession的工作示例。您可以阅读下面的集成基本步骤,但是建议您在与自己的应用程序集成时遵循详细的《 Hazelcast Spring 指南》。
Spring Configuration
添加所需的依赖关系之后,我们可以创建我们的 Spring 配置。 Spring 配置负责创建一个 Servlet 过滤器,该过滤器将HttpSession实现替换为 Spring Session 支持的实现。添加以下 Spring 配置:
@EnableHazelcastHttpSession (1)
@Configuration
public class HazelcastHttpSessionConfig {
@Bean
public HazelcastInstance hazelcastInstance() {
MapAttributeConfig attributeConfig = new MapAttributeConfig()
.setName(HazelcastSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)
.setExtractor(PrincipalNameExtractor.class.getName());
Config config = new Config();
config.getMapConfig("spring:session:sessions") (2)
.addMapAttributeConfig(attributeConfig)
.addMapIndexConfig(new MapIndexConfig(
HazelcastSessionRepository.PRINCIPAL_NAME_ATTRIBUTE, false));
return Hazelcast.newHazelcastInstance(config); (3)
}
}
- (1)
@EnableHazelcastHttpSessionComments 创建一个名称为springSessionRepositoryFilter的 Spring Bean,用于实现 Filter。过滤器负责替换由 Spring Session 支持的HttpSession实现。在这种情况下,Spring Session 由 Hazelcast 支持。 - (2) 为了支持通过主体名称索引检索会话,需要注册适当的
ValueExtractor。 Spring Session 为此提供了PrincipalNameExtractor。 - (3) 我们创建了一个
HazelcastInstance,用于将 Spring Session 与 Hazelcast 连接起来。默认情况下,Hazelcast 的嵌入式实例是由应用程序启动并连接的。有关配置 Hazelcast 的更多信息,请参阅reference documentation。
Servlet 容器初始化
我们的Spring Configuration创建了一个实现Filter的名为springSessionRepositoryFilter的 Spring Bean。 springSessionRepositoryFilter bean 负责用 Spring Session 支持的自定义实现替换HttpSession。
为了让我们的Filter发挥其魔力,Spring 需要加载我们的SessionConfig类。由于我们的应用程序已经在使用SecurityInitializer类加载 Spring 配置,因此我们可以简单地将SessionConfig类添加到其中。
src/main/java/sample/SecurityInitializer.java
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
public SecurityInitializer() {
super(SecurityConfig.class, SessionConfig.class);
}
}
最后,我们需要确保我们的 Servlet 容器(即 Tomcat)对每个请求都使用springSessionRepositoryFilter。在 Spring Security 的springSecurityFilterChain之前调用 Spring Session 的springSessionRepositoryFilter极为重要。这样可以确保 Spring Security 支持HttpSession。幸运的是,Spring Session 提供了一个名为AbstractHttpSessionApplicationInitializer的 Util 类,这使此操作非常容易。您可以在下面找到一个示例:
src/main/java/sample/Initializer.java
public class Initializer extends AbstractHttpSessionApplicationInitializer {
}
Note
我们的类(Initializer)的名称无关紧要。重要的是我们扩展AbstractHttpSessionApplicationInitializer。
通过扩展AbstractHttpSessionApplicationInitializer,我们确保在 Spring Security 的springSecurityFilterChain之前为每个请求向我们的 Servlet 容器注册名称为springSessionRepositoryFilter的 Spring Bean。
HttpSession 集成如何工作
幸运的是HttpSession和HttpServletRequest(用于获取HttpSession的 API)都是接口。这意味着我们可以为每个这些 API 提供自己的实现。
Note
本节描述 Spring Session 如何提供与HttpSession的透明集成。目的是使用户可以了解幕后情况。此功能已经集成,您无需自己实现此逻辑。
首先,我们创建一个自定义HttpServletRequest,它返回HttpSession的自定义实现。它看起来像以下内容:
public class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper {
public SessionRepositoryRequestWrapper(HttpServletRequest original) {
super(original);
}
public HttpSession getSession() {
return getSession(true);
}
public HttpSession getSession(boolean createNew) {
// create an HttpSession implementation from Spring Session
}
// ... other methods delegate to the original HttpServletRequest ...
}
任何返回HttpSession的方法都将被覆盖。所有其他方法都由HttpServletRequestWrapper实现,并仅委托给原始HttpServletRequest实现。
我们使用名为SessionRepositoryFilter的 servlet Filter替换HttpServletRequest实现。伪代码可以在下面找到:
public class SessionRepositoryFilter implements Filter {
public doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
SessionRepositoryRequestWrapper customRequest =
new SessionRepositoryRequestWrapper(httpRequest);
chain.doFilter(customRequest, response, chain);
}
// ...
}
通过将自定义HttpServletRequest实现传递到FilterChain中,我们确保Filter之后调用的所有内容都使用自定义HttpSession实现。这突出了为什么必须将 Spring Session 的SessionRepositoryFilter放置在与HttpSession进行交互的任何内容之前很重要。
在单个浏览器中有多个 HttpSession
Spring Session 能够在单个浏览器实例中支持多个会话。这提供了在同一浏览器实例(即 Google 帐户)中支持对多个用户进行身份验证的能力。
Note
Management 多个用户指南提供了在同一浏览器实例中 Management 多个用户的完整工作示例。您可以按照下面的基本集成步骤进行操作,但是在与自己的应用程序集成时,建议您遵循详细的《Management 多个用户指南》。
让我们看一下 Spring Session 如何跟踪多个会话。
Management 单个会话
Spring Session 通过将值添加到名为 SESSION 的 cookie 中来跟踪HttpSession。例如,SESSION cookie 的值可能为:
7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
添加会话
我们可以通过请求一个包含特殊参数的 URL 来添加另一个会话。默认情况下,参数名称为 _ s 。例如,以下 URL 将创建一个新会话:
Note
该参数值未指示实际的会话 ID。这很重要,因为我们永远不想让 Client 端确定会话 ID 以避免会话固定攻击。此外,我们不希望会话 ID 泄漏,因为它是作为查询参数发送的。请记住,敏感信息应仅作为 Headers 或在请求正文中传输。
除了自己创建 URL,我们还可以利用HttpSessionManager为我们完成此操作。我们可以使用以下方法从HttpServletRequest获得HttpSessionManager:
src/main/java/sample/UserAccountsFilter.java
HttpSessionManager sessionManager = (HttpSessionManager) httpRequest
.getAttribute(HttpSessionManager.class.getName());
现在,我们可以使用它来创建 URL 以添加另一个会话。
src/main/java/sample/UserAccountsFilter.java
String addAlias = unauthenticatedAlias == null ? (1)
sessionManager.getNewSessionAlias(httpRequest)
: (2)
unauthenticatedAlias; (3)
String addAccountUrl = sessionManager.encodeURL(contextPath, addAlias); (4)
- (1) 我们有一个名为
unauthenticatedAlias的现有变量。该值是一个别名,它指向现有的未经身份验证的会话。如果不存在这样的会话,则该值为 null。这可以确保我们是否有一个现有的未经身份验证的会话使用它,而不是创建一个新的会话。 - (2) 如果我们所有会话均已与用户相关联,则我们将创建一个新的会话别名。
- (3) 如果存在与用户无关的会话,我们将使用其会话别名。
- (4) 最后,我们创建添加帐户 URL。该 URL 包含一个会话别名,该会话别名要么指向现有的未经身份验证的会话,要么是一个未使用的别名,从而发出 signal 以创建与该别名相关联的新会话。
现在我们的 SESSION Cookie 看起来像这样:
0 7e8383a4-082c-4ffe-a4bc-c40fd3363c5e 1 1d526d4a-c462-45a4-93d9-84a39b6d44ad
Such that:
会话 ID 为 7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
该会话的别名为 0 。例如,如果 URL 为http://localhost:8080/?_s=0,则将使用此别名。
- 这是默认会话。这意味着,如果未指定会话别名,则使用此会话。例如,如果 URL 为http://localhost:8080/,则将使用此会话。
会话 ID 为 1d526d4a-c462-45a4-93d9-84a39b6d44ad
此会话的别名为 1 。如果会话别名为 1 ,则使用此会话。例如,如果 URL 为http://localhost:8080/?_s=1,则将使用此别名。
自动会话别名包含 encodeURL
在 URL 中指定会话别名的好处是,我们可以打开多个带有不同活动会话的选项卡。不好的是,我们需要在应用程序的每个 URL 中都包含会话别名。幸运的是,Spring Session 会在通过HttpServletResponse#encodeURL(java.lang.String)传递的任何 URL 中自动包含会话别名。
这意味着,如果您使用的是标准标记库,则会话别名将自动包含在 URL 中。例如,如果我们当前正在使用别名为 1 的会话,则执行以下操作:
src/main/webapp/index.jsp
-->
<c:url value="/link.jsp" var="linkUrl"/>
<a id="navLink" href="${linkUrl}">Link</a>
将输出以下链接:
<a id="navLink" href="/link.jsp?_s=1">Link</a>
HttpSession 和 RESTful API
通过允许在 Headers 中提供会话,Spring Session 可以与 RESTful API 一起使用。
Note
REST Sample提供了有关如何在 REST 应用程序中使用 Spring Session 来支持 Headers 身份验证的工作示例。您可以按照以下集成的基本步骤进行操作,但是在与自己的应用程序集成时,建议您遵循详细的 REST 指南。
Spring Configuration
添加所需的依赖关系之后,我们可以创建我们的 Spring 配置。 Spring 配置负责创建一个 Servlet 过滤器,该过滤器将HttpSession实现替换为 Spring Session 支持的实现。添加以下 Spring 配置:
@Configuration
@EnableRedisHttpSession (1)
public class HttpSessionConfig {
@Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory(); (2)
}
@Bean
public HttpSessionStrategy httpSessionStrategy() {
return new HeaderHttpSessionStrategy(); (3)
}
}
- (1)
@EnableRedisHttpSessionComments 创建一个名称为springSessionRepositoryFilter的 Spring Bean,实现Filter。过滤器负责替换由 Spring Session 支持的HttpSession实现。在这种情况下,Spring Session 由 Redis 支持。 - (2) 我们创建一个
RedisConnectionFactory,将 Spring Session 连接到 Redis Server。我们将连接配置为在默认端口(6379)上连接到 localhost。有关配置 Spring Data Redis 的更多信息,请参阅reference documentation。 - (3) 我们自定义了 Spring Session 的 HttpSession 集成,以使用 HTTP Headers 来传达当前会话信息,而不是 cookie。
Servlet 容器初始化
我们的Spring Configuration创建了一个实现Filter的名为springSessionRepositoryFilter的 Spring Bean。 springSessionRepositoryFilter bean 负责用 Spring Session 支持的自定义实现替换HttpSession。
为了让我们的Filter发挥其魔力,Spring 需要加载我们的Config类。我们在 Spring MvcInitializer中提供配置,如下所示:
src/main/java/sample/mvc/MvcInitializer.java
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { SecurityConfig.class, HttpSessionConfig.class };
}
最后,我们需要确保我们的 Servlet 容器(即 Tomcat)对每个请求都使用springSessionRepositoryFilter。幸运的是,Spring Session 提供了一个名为AbstractHttpSessionApplicationInitializer的 Util 类,这使得此操作非常容易。只需使用默认构造函数扩展该类,如下所示:
src/main/java/sample/Initializer.java
public class Initializer extends AbstractHttpSessionApplicationInitializer {
}
Note
我们的类(Initializer)的名称无关紧要。重要的是我们扩展AbstractHttpSessionApplicationInitializer。
HttpSessionListener
Spring Session pass 语句SessionEventHttpSessionListenerAdapter将SessionDestroyedEvent和SessionCreatedEvent转换为HttpSessionEvent来支持HttpSessionListener。要使用此支持,您需要:
确保您的
SessionRepository实现支持并配置为触发SessionDestroyedEvent和SessionCreatedEvent。将
SessionEventHttpSessionListenerAdapter配置为 Spring bean。将每个
HttpSessionListener注入SessionEventHttpSessionListenerAdapter
如果您使用的是带有 Redis 的 HttpSession中记录的配置支持,则只需将每个HttpSessionListener注册为 Bean。例如,假设您要支持 Spring Security 的并发控制,并且需要使用HttpSessionEventPublisher,则只需将HttpSessionEventPublisher添加为 bean。在 Java 配置中,这可能类似于:
@Configuration
@EnableRedisHttpSession
public class RedisHttpSessionConfig {
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
// ...
}
在 XML 配置中,这可能类似于:
<bean class="org.springframework.security.web.session.HttpSessionEventPublisher"/>
WebSocket Integration
Spring Session 提供了与 Spring 的 WebSocket 支持的透明集成。
Note
Spring Session 的 WebSocket 支持仅与 Spring 的 WebSocket 支持一起使用。具体来说,它不能直接与JSR-356一起使用。这是由于 JSR-356 没有用于拦截传入的 WebSocket 消息的机制。
为什么使用 Spring Session 和 WebSockets?
那么,为什么在使用 WebSockets 时需要 Spring Session?
考虑一个通过 HTTP 请求完成其大部分工作的电子邮件应用程序。但是,其中还嵌入了一个可通过 WebSocket API 运行的聊天应用程序。如果用户正在积极地与某人聊天,则我们不应使HttpSession超时,因为这会带来非常糟糕的用户体验。但是,这正是JSR-356所做的。
另一个问题是,根据 JSR-356,如果HttpSession超时,则将使用该 HttpSession 创建的任何 WebSocket 超时,并且应强制关闭经过身份验证的用户。这意味着,如果我们正在应用程序中积极地聊天而不使用 HttpSession,那么我们也将断开与对话的连接!
WebSocket Usage
WebSocket Sample提供了有关如何将 Spring Session 与 WebSocket 集成的工作示例。您可以按照下面的基本集成步骤进行操作,但是在与自己的应用程序集成时,建议您遵循详细的 WebSocket 指南:
HttpSession Integration
使用 WebSocket 集成之前,应确保首先工作HttpSession Integration。
Spring Configuration
在典型的 Spring WebSocket 应用程序中,用户将扩展AbstractWebSocketMessageBrokerConfigurer。例如,配置可能类似于以下内容:
@Configuration
@EnableScheduling
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/messages").withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/queue/", "/topic/");
registry.setApplicationDestinationPrefixes("/app");
}
}
我们可以轻松地更新配置以使用 Spring Session 的 WebSocket 支持。例如:
src/main/java/samples/config/WebSocketConfig.java
@Configuration
@EnableScheduling
@EnableWebSocketMessageBroker
public class WebSocketConfig
extends AbstractSessionWebSocketMessageBrokerConfigurer<ExpiringSession> { (1)
protected void configureStompEndpoints(StompEndpointRegistry registry) { (2)
registry.addEndpoint("/messages").withSockJS();
}
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/queue/", "/topic/");
registry.setApplicationDestinationPrefixes("/app");
}
}
要获得 Spring Session 支持,我们只需要更改两件事:
- (1) 我们没有扩展
AbstractWebSocketMessageBrokerConfigurer,而是扩展了AbstractSessionWebSocketMessageBrokerConfigurer - (2) 我们将
registerStompEndpoints方法重命名为configureStompEndpoints
AbstractSessionWebSocketMessageBrokerConfigurer在幕后做什么?
WebSocketConnectHandlerDecoratorFactory作为WebSocketHandlerDecoratorFactory添加到WebSocketTransportRegistration。这样可以确保触发包含WebSocketSession的自定义SessionConnectEvent。WebSocketSession是终止 Spring Session 终止时仍打开的任何 WebSocket 连接所必需的。SessionRepositoryMessageInterceptor作为HandshakeInterceptor添加到每个StompWebSocketEndpointRegistration中。这样可以确保将 Session 添加到 WebSocket 属性以启用更新上次访问时间。SessionRepositoryMessageInterceptor作为ChannelInterceptor添加到我们的入站ChannelRegistration中。这样可以确保每次接收到入站消息时,Spring Session 的上次访问时间都会更新。WebSocketRegistryListener被创建为 Spring Bean。这样可以确保我们将所有会话 IDMap 到相应的 WebSocket 连接。通过维护此 Map,我们可以在 Spring Session(HttpSession)终止时关闭所有 WebSocket 连接。
Spring 安全集成
Spring Session 提供与 Spring Security 的集成。
Spring Security 记住我支持
Spring Session 提供与Spring Security 的“记住我”身份验证的集成。支持将:
更改会话过期时间
确保会话 cookie 在
Integer.MAX_VALUE过期。 cookie 过期设置为最大可能值,因为仅在创建会话时才设置 cookie。如果将其设置为与会话到期相同的值,则会话将在用户使用会话时进行更新,但 cookie 到期将不会更新,导致到期被固定。
要在 Java Configuration 中使用 Spring Security 配置 Spring Session,请使用以下指南:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// ... additional configuration ...
.rememberMe()
.rememberMeServices(rememberMeServices());
}
@Bean
public SpringSessionRememberMeServices rememberMeServices() {
SpringSessionRememberMeServices rememberMeServices =
new SpringSessionRememberMeServices();
// optionally customize
rememberMeServices.setAlwaysRemember(true);
return rememberMeServices;
}
基于 XML 的配置如下所示:
<security:http>
<!-- ... -->
<security:form-login />
<security:remember-me services-ref="rememberMeServices"/>
</security:http>
<bean id="rememberMeServices"
class="org.springframework.session.security.web.authentication.SpringSessionRememberMeServices"
p:alwaysRemember="true"/>
Spring Security 并发会话控制
Spring Session 提供了与 Spring Security 的集成,以支持其并发会话控制。这允许限制单个用户可以同时具有的活动会话数,但是与默认的 Spring Security 支持不同,这也可以在集群环境中工作。这是通过提供 Spring Security 的SessionRegistry接口的自定义实现来完成的。
使用 Spring Security 的 Java 配置 DSL 时,您可以像这样通过SessionManagementConfigurer配置自定义SessionRegistry:
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
FindByIndexNameSessionRepository<ExpiringSession> sessionRepository;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// other config goes here...
.sessionManagement()
.maximumSessions(2)
.sessionRegistry(sessionRegistry());
}
@Bean
SpringSessionBackedSessionRegistry sessionRegistry() {
return new SpringSessionBackedSessionRegistry(this.sessionRepository);
}
}
假设您还配置了 Spring Session 以提供一个FindByIndexNameSessionRepository返回ExpiringSession实例。
使用 XML 配置时,它看起来像这样:
<security:http>
<!-- other config goes here... -->
<security:session-management>
<security:concurrency-control max-sessions="2" session-registry-ref="sessionRegistry"/>
</security:session-management>
</security:http>
<bean id="sessionRegistry"
class="org.springframework.session.security.SpringSessionBackedSessionRegistry">
<constructor-arg ref="sessionRepository"/>
</bean>
假设您的 Spring Session SessionRegistry bean 名为sessionRegistry,这是所有SpringHttpSessionConfiguration子类使用的名称,但 MongoDB 的子类除外:那里称为mongoSessionRepository。
Limitations
Spring Session 的 Spring Security 的SessionRegistry接口实现不支持getAllPrincipals方法,因为无法使用 Spring Session 检索此信息。 Spring Security 从不调用此方法,因此这只会影响自己访问SessionRegistry的应用程序。
API Documentation
您可以在线浏览完整的Javadoc。关键 API 如下所述:
Session
Session是名称值对的简化Map。
典型用法如下所示:
public class RepositoryDemo<S extends Session> {
private SessionRepository<S> repository; (1)
public void demo() {
S toSave = this.repository.createSession(); (2)
(3)
User rwinch = new User("rwinch");
toSave.setAttribute(ATTR_USER, rwinch);
this.repository.save(toSave); (4)
S session = this.repository.getSession(toSave.getId()); (5)
(6)
User user = session.getAttribute(ATTR_USER);
assertThat(user).isEqualTo(rwinch);
}
// ... setter methods ...
}
- (1) 我们创建一个通用类型
S的SessionRepository实例,该实例扩展了Session。泛型类型在我们的类中定义。 - (2) 我们使用
SessionRepository创建一个新的Session并将其分配给S类型的变量。 - (3) 我们与
Session互动。在我们的示例中,我们演示了将User保存到Session。 - (4) 我们现在保存
Session。这就是为什么我们需要通用类型S的原因。SessionRepository仅允许保存使用相同SessionRepository创建或检索的Session实例。这允许SessionRepository进行特定于实现的优化(即仅写入已更改的属性)。 - (5) 我们从
SessionRepository检索Session。 - (6) 我们从
Session获得了持久化的User,而无需显式转换属性。
ExpiringSession
ExpiringSession通过提供与Session实例的到期相关的属性来扩展Session。如果不需要与到期信息进行交互,则最好使用更简单的Session API。
典型用法如下所示:
public class ExpiringRepositoryDemo<S extends ExpiringSession> {
private SessionRepository<S> repository; (1)
public void demo() {
S toSave = this.repository.createSession(); (2)
// ...
toSave.setMaxInactiveIntervalInSeconds(30); (3)
this.repository.save(toSave); (4)
S session = this.repository.getSession(toSave.getId()); (5)
// ...
}
// ... setter methods ...
}
- (1) 我们创建一个通用类型
S的SessionRepository实例,该实例扩展了ExpiringSession。泛型类型在我们的类中定义。 - (2) 我们使用
SessionRepository创建一个新的ExpiringSession并将其分配给S类型的变量。 - (3) 我们与
ExpiringSession互动。在我们的示例中,我们演示了更新ExpiringSession可以在其失效之前处于非活动状态的时间。 - (4) 我们现在保存
ExpiringSession。这就是为什么我们需要通用类型S的原因。SessionRepository仅允许保存使用相同SessionRepository创建或检索的ExpiringSession实例。这允许SessionRepository进行特定于实现的优化(即仅写入已更改的属性)。保存ExpiringSession后,上次访问的时间会自动更新。 - (5) 我们从
SessionRepository检索ExpiringSession。如果ExpiringSession过期,则结果为 null。
SessionRepository
SessionRepository负责创建,检索和保留Session实例。
如果可能,开发人员不应直接与SessionRepository或Session进行交互。相反,开发人员应该更喜欢通过HttpSession和WebSocket集成与SessionRepository和Session间接交互。
FindByIndexNameSessionRepository
Spring Session 使用Session的最基本 API 是SessionRepository。该 API 故意非常简单,因此很容易为其他实现提供基本功能。
一些SessionRepository实现可能也选择实现FindByIndexNameSessionRepository。例如,Spring 的 Redis 支持实现FindByIndexNameSessionRepository。
FindByIndexNameSessionRepository添加了一种方法来查找特定用户的所有会话。通过确保使用用户名填充名称为FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME的会话属性来完成此操作。由于 Spring Session 不知道所使用的身份验证机制,因此开发人员有责任确保填充属性。下面显示了如何使用此示例:
String username = "username";
this.session.setAttribute(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username);
Note
FindByIndexNameSessionRepository的某些实现将提供钩子以自动索引其他会话属性。例如,许多实现将自动确保当前的 Spring Security 用户名使用索引名FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME进行索引。
会话构建索引后,可以使用以下命令找到它:
String username = "username";
Map<String, Session> sessionIdToSession = this.sessionRepository
.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
username);
EnableSpringHttpSession
可以将@EnableSpringHttpSession注解添加到@Configuration类,以将SessionRepositoryFilter作为名为“ springSessionRepositoryFilter”的 bean 公开。为了利用 Comments,必须提供单个SessionRepository bean。例如:
@EnableSpringHttpSession
@Configuration
public class SpringHttpSessionConfig {
@Bean
public MapSessionRepository sessionRepository() {
return new MapSessionRepository();
}
}
重要的是要注意,没有为您配置开箱即用的会话过期基础结构。这是因为会话期满之类的东西在很大程度上取决于实现。这意味着,如果您需要清理过期的会话,则您有责任清理过期的会话。
RedisOperationsSessionRepository
RedisOperationsSessionRepository是使用 Spring Data 的RedisOperations实现的SessionRepository。在 Web 环境中,通常与SessionRepositoryFilter结合使用。该实现支持SessionDestroyedEvent和SessionCreatedEvent至SessionMessageListener。
实例化 RedisOperationsSessionRepository
下面是如何创建新实例的典型示例:
LettuceConnectionFactory factory = new LettuceConnectionFactory();
SessionRepository<? extends ExpiringSession> repository = new RedisOperationsSessionRepository(
factory);
有关如何创建RedisConnectionFactory的其他信息,请参阅《 Spring Data Redis 参考》。
EnableRedisHttpSession
在 Web 环境中,创建新RedisOperationsSessionRepository的最简单方法是使用@EnableRedisHttpSession。完整的示例用法可在samples 和指南(从这里开始)中找到。您可以使用以下属性来自定义配置:
maxInactiveIntervalInSeconds -会话过期前的时间(以秒为单位)
redisNamespace –允许为会话配置应用程序特定的名称空间。 Redis 键和通道 ID 的前缀为
spring:session:<redisNamespace>:。redisFlushMode -允许指定何时将数据写入 Redis。仅当在
SessionRepository上调用save时才是默认值。值RedisFlushMode.IMMEDIATE将尽快写入 Redis。
Custom RedisSerializer
您可以通过创建一个实现springSessionDefaultRedisSerializer的名为springSessionDefaultRedisSerializer的 Bean 来自定义序列化。
Redis TaskExecutor
订阅RedisOperationsSessionRepository以使用RedisMessageListenerContainer从 redis 接收事件。您可以通过创建名为springSessionRedisTaskExecutor和/或 Bean springSessionRedisSubscriptionExecutor的 Bean 来自定义这些事件的调度方式。有关配置 Redis 任务执行程序的更多详细信息,请参见here。
Storage Details
以下各节概述了如何为每个操作更新 Redis。可以在下面找到创建新会话的示例。以下各节描述了详细信息。
HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe creationTime 1404360000000 \
maxInactiveInterval 1800 \
lastAccessedTime 1404360000000 \
sessionAttr:attrName someAttrValue \
sessionAttr2:attrName someAttrValue2
EXPIRE spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe 2100
APPEND spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe ""
EXPIRE spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe 1800
SADD spring:session:expirations:1439245080000 expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe
EXPIRE spring:session:expirations1439245080000 2100
保存会话
每个会话都作为哈希存储在 Redis 中。使用 HMSET 命令设置和更新每个会话。每个会话如何存储的示例如下所示。
HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe creationTime 1404360000000 \
maxInactiveInterval 1800 \
lastAccessedTime 1404360000000 \
sessionAttr:attrName someAttrValue \
sessionAttr2:attrName someAttrValue2
在此示例中,关于会话的以下语句是正确的:
会话 ID 为 33fdd1b6-b496-4b33-9f7d-df96679d32fe
该会话的创建时间为格林尼治标准时间 1970 年 1 月 1 日午夜以来的 1404360000000.
会话将在 1800 秒(30 分钟)后过期。
自格林尼治标准时间 1970 年 1 月 1 日午夜以来,该会话的最后访问时间为 1404360000000.
会话具有两个属性。第一个是值为“ someAttrValue”的“ attrName”。第二个会话属性被命名为“ attrName2”,其值为“ someAttrValue2”。
Optimized Writes
由RedisOperationsSessionRepositoryManagement 的Session实例会跟踪已更改的属性,并且只会更新这些属性。这意味着,如果一次写入一个属性并读取多次,我们只需要写入一次该属性。例如,假设先前的会话属性“ sessionAttr2”已更新。保存后将执行以下操作:
HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe sessionAttr:attrName2 newValue
Session Expiration
使用基于ExpiringSession.getMaxInactiveInterval()的 EXPIRE 命令将到期与每个会话相关联。例如:
EXPIRE spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe 2100
您将注意到,所设置的到期时间是会话实际到期后的 5 分钟。这是必需的,以便在会话过期时可以访问该会话的值。会话本身实际上会过期五分钟后才在会话本身上设置一个过期,以确保将其清除,但是仅在我们执行了必要的处理之后。
Note
SessionRepository.getSession(String)方法可确保不会返回任何过期的会话。这意味着在使用会话之前无需检查到期时间。
Spring Session 依靠从 Redis 删除和过期keyspace notifications分别触发SessionDeletedEvent和SessionExpiredEvent。 SessionDeletedEvent或SessionExpiredEvent确保与会话相关联的资源被清除。例如,当使用 Spring Session 的 WebSocket 支持时,Redis 过期或删除事件将触发与该会话关联的所有 WebSocket 连接被关闭。
不会直接在会话密钥本身上跟踪到期时间,因为这将意味着会话数据将不再可用。而是使用特殊的会话过期密钥。在我们的示例中,expires 键是:
APPEND spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe ""
EXPIRE spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe 1800
当会话过期时,密钥将被删除或过期,密钥空间通知将触发对实际会话的查找,并触发 SessionDestroyedEvent。
完全依赖 Redis 到期的一个问题是,Redis 无法保证如果尚未访问密钥,则何时触发过期事件。特别是,Redis 用于清除过期密钥的后台任务是低优先级任务,可能不会触发密钥过期。有关更多详细信息,请参见 Redis 文档中的过期事件的时间部分。
为了规避不能保证发生过期事件的事实,我们可以确保在预期每个密钥都到期时可以访问每个密钥。这意味着,如果密钥上的 TTL 过期,当我们尝试访问密钥时,Redis 将删除密钥并触发过期事件。
因此,每个会话的到期时间也会被追踪到最近的分钟。这允许后台任务访问可能过期的会话,以确保以更确定的方式触发 Redis 过期事件。例如:
SADD spring:session:expirations:1439245080000 expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe
EXPIRE spring:session:expirations1439245080000 2100
然后,后台任务将使用这些 Map 来显式请求每个键。通过访问密钥而不是删除密钥,我们确保 Redis 仅在 TTL 过期时才为我们删除密钥。
Note
我们不会显式删除密钥,因为在某些情况下,可能会出现竞争情况,错误地将密钥标识为未过期。缺少使用分布式锁(这会损害我们的性能)的方法无法确保到期 Map 的一致性。通过简单地访问密钥,我们确保仅在该密钥上的 TTL 过期时才删除该密钥。
SessionDeletedEvent 和 SessionExpiredEvent
SessionDeletedEvent和SessionExpiredEvent都是SessionDestroyedEvent的类型。
RedisOperationsSessionRepository支持在删除Session或在SessionExpiredEvent过期时触发SessionDeletedEvent。这是确保正确清理与Session关联的资源所必需的。
例如,与 WebSockets 集成时,SessionDestroyedEvent负责关闭所有活动的 WebSocket 连接。
可以通过监听Redis 键空间事件的SessionMessageListener来触发SessionDeletedEvent或SessionExpiredEvent。为了使其正常工作,需要启用通用命令的 Redis 键空间事件和过期事件。例如:
redis-cli config set notify-keyspace-events Egx
如果您使用的是@EnableRedisHttpSession和SessionMessageListener并启用必要的 Redis Keyspace 事件,则会自动完成。但是,在安全的 Redis 环境中,禁用 config 命令。这意味着 Spring Session 无法为您配置 Redis Keyspace 事件。要禁用自动配置,请将ConfigureRedisAction.NO_OP作为 bean 添加。
例如,Java 配置可以使用以下内容:
@Bean
public static ConfigureRedisAction configureRedisAction() {
return ConfigureRedisAction.NO_OP;
}
XML 配置可以使用以下内容:
<util:constant
static-field="org.springframework.session.data.redis.config.ConfigureRedisAction.NO_OP"/>
SessionCreatedEvent
创建会话后,将使用spring:session:channel:created:33fdd1b6-b496-4b33-9f7d-df96679d32fe通道将事件发送到 Redis,使得33fdd1b6-b496-4b33-9f7d-df96679d32fe是会话 ID。事件的主体将是创建的会话。
如果注册为 MessageListener(默认),则RedisOperationsSessionRepository会将 Redis 消息转换为SessionCreatedEvent。
在 Redis 中查看会话
installing redis-cli之后,您可以检查 Redis 使用 redis-cli中的值。例如,在终端中 Importing 以下内容:
$ redis-cli
redis 127.0.0.1:6379> keys *
1) "spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed96fb021" (1)
2) "spring:session:expirations:1418772300000" (2)
- (1) 此项的后缀是 Spring Session 的会话标识符。
- (2) 此项包含在
1418772300000时间应删除的所有会话 ID。
您还可以查看每个会话的属性。
redis 127.0.0.1:6379> hkeys spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed96fb021
1) "lastAccessedTime"
2) "creationTime"
3) "maxInactiveInterval"
4) "sessionAttr:username"
redis 127.0.0.1:6379> hget spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed96fb021 sessionAttr:username
"\xac\xed\x00\x05t\x00\x03rob"
GemFireOperationsSessionRepository
GemFireOperationsSessionRepository是使用 Spring Data 的GemFireOperationsSessionRepository实现的SessionRepository。在 Web 环境中,通常与SessionRepositoryFilter结合使用。该实现支持SessionDestroyedEvent和SessionCreatedEvent至SessionMessageListener。
通过 GemFire 使用索引
尽管有关对索引正确定义产生积极影响 GemFire 性能的最佳做法不在本文档的范围内,但重要的是要认识到 Spring Session Data GemFire 创建并使用索引来有效地查询和查找 Session。
开箱即用的 Spring Session 数据 GemFire 在主体名称上创建 1 个哈希类型的索引。查找主体名称的策略有两种不同的选择。第一种策略是将名称为FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME的 session 属性的值索引到相同的索引名称。例如:
String indexName = FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME;
session.setAttribute(indexName, username);
Map<String, ExpiringSession> idToSessions = sessionRepository
.findByIndexNameAndIndexValue(indexName, username);
通过 GemFire 和 Spring Security 使用索引
另外,Spring Session Data GemFire 会将 Spring Security 的当前Authentication#getName()Map 到索引FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME。例如,如果您使用的是 Spring Security,则可以使用以下命令查找当前用户的会话:
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
String indexName = FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME;
Map<String, ExpiringSession> idToSessions = sessionRepository
.findByIndexNameAndIndexValue(indexName, authentication.getName());
通过 GemFire 使用自定义索引
这使开发人员能够以编程方式使用GemFireOperationsSessionRepository来有效地查询和查找具有给定主体名称的所有 Session。
此外,当开发人员识别出应由 GemFire 索引的 1 个或多个命名 Session 属性时,Spring Session Data GemFire 将在实现的 Session 的 Map-type attributes属性(即,在任意 Session 属性上)上创建基于范围的索引。
可以使用@EnableGemFireHttpSession注解上的indexableSessionAttributes属性指定要索引的会话属性。当他/她希望为由 GemFire 支持的 HttpSession 启用 Spring Session 支持时,开发人员将此 Comments 添加到其 Spring 应用程序@Configuration类中。
例如,以下配置:
@EnableGemFireHttpSession(indexableSessionAttributes = { "name1", "name2", "name3" })
public class GemFireHttpSessionConfig {
// ...
}
将允许使用以下内容搜索会话:
String indexName = "name1";
session.setAttribute(indexName, attrValue);
Map<String, ExpiringSession> idToSessions = sessionRepository
.findByIndexNameAndIndexValue(indexName, attrValue);
Note
只有在@EnableGemFireHttpSessionComments 的indexableSessionAttributes属性中标识的会话属性名称才定义索引。所有其他会话属性将不被索引。
但是,有一个警告。存储在可索引 Session 属性中的任何值都必须实现java.lang.Comparable<T>接口。如果这些对象值未实现Comparable,则当为具有持久性 Session 数据的 Region 定义了 Index,或者在运行时尝试为可索引 Session 属性分配的值不是Comparable时,GemFire 将在启动时引发错误。会话将保存到 GemFire。
Note
任何未构建索引的会话属性都可以存储非Comparable的值。
要了解有关 GemFire 的基于范围的索引的更多信息,请参阅在 Map 字段上创建索引。
要总体上了解有关 GemFire 索引的更多信息,请参阅使用索引。
MapSessionRepository
MapSessionRepository允许将ExpiringSession保留在Map中,其中键为ExpiringSession id,值为ExpiringSession。该实现可以与ConcurrentHashMap一起用作测试或便利机制。或者,它可以与分布式Map实现一起使用。例如,它可以与 Hazelcast 一起使用。
Instantiating MapSessionRepository
创建一个新实例非常简单:
SessionRepository<? extends ExpiringSession> repository = new MapSessionRepository();
使用 Spring Session 和 Hazlecast
Hazelcast Sample是一个完整的应用程序,演示了将 Spring Session 与 Hazelcast 一起使用。
要运行它,请使用以下命令:
./gradlew :samples:hazelcast:tomcatRun
榛果 Springsample是一个完整的应用程序,演示了将 Spring Session 与 Hazelcast 和 Spring Security 一起使用。
它包括示例 Hazelcast MapListener实现,它们支持触发SessionCreatedEvent,SessionDeletedEvent和SessionExpiredEvent。
要运行它,请使用以下命令:
./gradlew :samples:hazelcast-spring:tomcatRun
JdbcOperationsSessionRepository
JdbcOperationsSessionRepository是SessionRepository实现,它使用 Spring 的JdbcOperations将会话存储在关系数据库中。在 Web 环境中,通常与SessionRepositoryFilter结合使用。请注意,此实现不支持发布会话事件。
实例化 JdbcOperationsSessionRepository
下面是如何创建新实例的典型示例:
JdbcTemplate jdbcTemplate = new JdbcTemplate();
// ... configure JdbcTemplate ...
PlatformTransactionManager transactionManager = new DataSourceTransactionManager();
// ... configure transactionManager ...
SessionRepository<? extends ExpiringSession> repository =
new JdbcOperationsSessionRepository(jdbcTemplate, transactionManager);
有关如何创建和配置JdbcTemplate和PlatformTransactionManager的其他信息,请参考Spring 框架参考文档。
EnableJdbcHttpSession
在 Web 环境中,创建新JdbcOperationsSessionRepository的最简单方法是使用@EnableJdbcHttpSession。完整的示例用法可在samples 和指南(从这里开始)中找到。您可以使用以下属性来自定义配置:
tableName -Spring Session 用于存储会话的数据库表的名称
maxInactiveIntervalInSeconds -会话过期前的时间(以秒为单位)
Custom LobHandler
您可以通过创建实现springSessionLobHandler的名为springSessionLobHandler的 Bean 来自定义 BLOB 处理。
Custom ConversionService
您可以通过提供ConversionService实例来自定义会话的默认序列化和反序列化。在典型的 Spring 环境中工作时,默认的ConversionService Bean(名为conversionService)将被自动拾取并用于序列化和反序列化。但是,您可以通过提供一个名为springSessionConversionService的 Bean 来覆盖默认的ConversionService。
Storage Details
默认情况下,此实现使用SPRING_SESSION和SPRING_SESSION_ATTRIBUTES表存储会话。注意,表名可以很容易地自定义,如前所述。在这种情况下,将使用提供的表名(后缀_ATTRIBUTES)来命名用于存储属性的表。如果需要进一步的自定义,则可以使用set*Query setter 方法自定义存储库使用的 SQL 查询。在这种情况下,您需要手动配置sessionRepository bean。
由于各个数据库供应商之间的差异,尤其是在存储二进制数据时,请确保使用特定于数据库的 SQL 脚本。大多数主要数据库供应商的脚本打包为org/springframework/session/jdbc/schema-*.sql,其中*是目标数据库类型。
例如,对于 PostgreSQL 数据库,您将使用以下模式脚本:
CREATE TABLE SPRING_SESSION (
SESSION_ID CHAR(36) NOT NULL,
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (SESSION_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (LAST_ACCESS_TIME);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (PRINCIPAL_NAME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_ID CHAR(36) NOT NULL,
ATTRIBUTE_NAME VARCHAR(200) NOT NULL,
ATTRIBUTE_BYTES BYTEA NOT NULL,
CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_ID, ATTRIBUTE_NAME),
CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_ID) REFERENCES SPRING_SESSION(SESSION_ID) ON DELETE CASCADE
);
CREATE INDEX SPRING_SESSION_ATTRIBUTES_IX1 ON SPRING_SESSION_ATTRIBUTES (SESSION_ID);
并带有 MySQL 数据库:
CREATE TABLE SPRING_SESSION (
SESSION_ID CHAR(36) NOT NULL,
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (SESSION_ID)
) ENGINE=InnoDB;
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (LAST_ACCESS_TIME);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (PRINCIPAL_NAME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_ID CHAR(36) NOT NULL,
ATTRIBUTE_NAME VARCHAR(200) NOT NULL,
ATTRIBUTE_BYTES BLOB NOT NULL,
CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_ID, ATTRIBUTE_NAME),
CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_ID) REFERENCES SPRING_SESSION(SESSION_ID) ON DELETE CASCADE
) ENGINE=InnoDB;
CREATE INDEX SPRING_SESSION_ATTRIBUTES_IX1 ON SPRING_SESSION_ATTRIBUTES (SESSION_ID);
Transaction management
JdbcOperationsSessionRepository中的所有 JDBC 操作都以事务方式执行。在传播设置为REQUIRES_NEW的情况下执行事务,以避免由于对现有事务的干扰而导致意外行为(例如,在已经参与只读事务的线程中执行save操作)。
HazelcastSessionRepository
HazelcastSessionRepository是SessionRepository实现,它将会话存储在 Hazelcast 的分布式IMap中。在 Web 环境中,通常与SessionRepositoryFilter结合使用。
实例化 HazelcastSessionRepositories
下面是如何创建新实例的典型示例:
Config config = new Config();
// ... configure Hazelcast ...
HazelcastInstance hazelcastInstance = Hazelcast.newHazelcastInstance(config);
IMap<String, MapSession> sessions = hazelcastInstance
.getMap("spring:session:sessions");
HazelcastSessionRepository repository =
new HazelcastSessionRepository(sessions);
有关如何创建和配置 Hazelcast 实例的其他信息,请参阅Hazelcast documentation。
EnableHazelcastHttpSession
如果您希望使用Hazelcast作为SessionRepository的后备源,则可以将@EnableHazelcastHttpSessionComments 添加到@Configuration类中。这扩展了@EnableSpringHttpSession注解提供的功能,但在 Hazelcast 中为您提供了SessionRepository。您必须提供一个HazelcastInstance bean 才能使配置生效。完整的配置示例可以在samples 和指南(从这里开始)中找到
Storage Details
会话将使用MapSessionRepository存储在 Hazelcast 中的分布式IMap中。 IMap接口方法将用于get()和put()会话。此外,values()方法用于支持FindByIndexNameSessionRepository#findByIndexNameAndIndexValue操作,以及需要在 Hazelcast 中注册的适当的ValueExtractor。有关此配置的更多详细信息,请参考榛果 Springsample。 IMap中会话的到期由 Hazelcast 支持,用于设置将条目put()插入IMap时条目的生存时间。闲置时间超过生存时间的条目(会话)将自动从IMap中删除。
您无需在 Hazelcast 配置中为IMap配置任何设置,例如max-idle-seconds或time-to-live-seconds。
Basic Customization
您可以在@EnableHazelcastHttpSession上使用以下属性来自定义配置:
maxInactiveIntervalInSeconds -会话过期之前的时间(以秒为单位)。默认值为 1800 秒(30 分钟)
sessionMapName -分布式
Map的名称,将在 Hazelcast 中用于存储会话数据。
Session Events
使用MapListener响应添加,删除和从分布式Map中删除的条目,这些事件将分别触发使用ApplicationEventPublisher来发布 SessionCreatedEvent,SessionExpiredEvent 和 SessionDeletedEvent 事件。
Spring Session 社区
我们很高兴将您视为我们社区的一部分。请在下面找到更多信息。
Support
您可以通过在带标签 spring-session 的 StackOverflow上提问来获得帮助。同样,我们鼓励通过回答有关 StackOverflow 的问题来帮助他人。
Source Code
我们的源代码可以在 github 上找到https://github.com/spring-projects/spring-session/
Issue Tracking
我们在https://github.com/spring-projects/spring-session/issues跟踪 github 问题中的问题
Contributing
我们感谢Pull Requests。
License
Spring Session 是在Apache 2.0 许可证下发布的开源软件。
Community Extensions
| Name | Location |
|---|---|
| Spring Session OrientDB | https://github.com/maseev/spring-session-orientdb |
| Spring Session Infinispan | http://infinispan.org/docs/dev/user_guide/user_guide.html#externalizing_session_using_spring_session |
Minimum Requirements
Spring Session 的最低要求是:
Java 5+
如果您在 Servlet 容器中运行(不是必需的),则 Servlet 2.5
如果您正在使用其他 Spring 库(不需要),则最低要求的版本是 Spring 3.2.14. 在针对 Spring 3.2.x 重新运行所有单元测试时,我们建议尽可能使用最新的 Spring 4.x 版本。
@EnableRedisHttpSession需要 Redis 2.8. 这对于支持Session Expiration是必要的
Note
在 Spring Session 的核心部分,仅需要公共记录相关性。有关使用不带任何其他 Spring 依赖项的 Spring Session 的示例,请参阅hazelcast sample应用程序。