HttpSession Integration

Spring Session 与 HttpSession 提供透明集成。这意味着开发人员可以将 HttpSession 实现替换为由 Spring Session 支持的实现。

Why Spring Session and HttpSession?

我们已经提到 Spring Session 与 HttpSession 提供透明集成,但我们从中获得了哪些好处?

  • Clustered Sessions: Spring 会话轻松支持clustered sessions,而无需绑定到特定于应用程序容器的解决方案。

  • RESTful APIs: Spring 会话允许在标头中提供会话 ID 以配合RESTful APIs工作

HttpSession with Redis

HttpSession 中使用 Spring Session 是通过在所有使用 HttpSession 的内容之前添加 Servlet Filter 来实现的。您可以选择通过使用以下任一种方式来启用此功能:

Redis Java-based Configuration

本节介绍如何使用基于 Java 的配置通过 Redis 来支持 HttpSession

HttpSession Sample 提供了一个关于如何通过 Java 配置整合 Spring Session 和 HttpSession 的工作示例。你可以在接下来的几节中阅读整合的基本步骤,但是我们鼓励你在使用自己的应用程序进行整合时遵循 HttpSession 指南的具体内容。

@11

Redis XML-based Configuration

本节介绍如何使用基于 XML 的配置通过 Redis 来支持 HttpSession

HttpSession XML Sample 提供了一个关于如何通过 XML 配置整合 Spring Session 和 HttpSession 的工作示例。你可以在接下来的几节中阅读整合的基本步骤,但是我们鼓励你在使用自己的应用程序进行整合时遵循 HttpSession XML 指南的具体内容。

@12

HttpSession with Mongo

使用 `HttpSession`的 Spring Session 可通过在使用 `HttpSession`的任何内容之前添加一个 Servlet 过滤器来启用。

本部分介绍如何使用 Mongo 来基于 Java 配置支持 HttpSession

HttpSession Mongo Sample 提供了一个使用 Java 配置集成 Spring Session 和 HttpSession 的工作示例。您可以阅读以下集成的基本步骤,但我们建议您在与自己的应用程序集成时遵循详细的 HttpSession 指南。

@13

Session serialization mechanisms

为了能够在 MongoDB 中保存会话对象,我们需要提供序列化/反序列化机制。

默认情况下,Spring Session MongoDB 将使用 JdkMongoSessionConverter

然而,你可以通过简单的将以下代码添加到你的 Boot 应用来切换到 JacksonMongoSessionConverter

@Bean
JacksonMongoSessionConverter mongoSessionConverter() {
    return new JacksonMongoSessionConverter();
}
JacksonMongoSessionConverter

此机制使用 Jackson 将会话对象序列化为/从 JSON。

通过创建以下 bean:

@Bean
JacksonMongoSessionConverter mongoSessionConverter() {
    return new JacksonMongoSessionConverter();
}

…您能够从默认的(基于 JDK 的序列化)切换到使用 Jackson。

如果您要与 Spring Security 集成(通过在 MongoDB 中存储会话),该配置将注册合适的白名单组件,以便 Spring Security 正常工作。

如果您想提供自定义 Jackson 模块,您可以通过明确注册模块来执行此操作,如下所示:

Unresolved include directive in modules/ROOT/pages/http-session.adoc - include::example$spring-session-data-mongodb-dir/src/integration-test/java/org/springframework/session/data/mongo/integration/MongoRepositoryJacksonITest.java[]
JdkMongoSessionConverter

JdkMongoSessionConverter 使用标准的 Java 序列化以二进制形式将会话属性映射持久性存储在 MongoDB 中。然而,诸如 id、访问时间等标准会话元素仍作为普通的 Mongo 对象进行写入,并且可以在不需要额外工作的情况下进行读取和查询。如果未定义显式的 AbstractMongoSessionConverter Bean,将使用 JdkMongoSessionConverter

还存在一个使用 SerializerDeserializer 对象的构造函数,它允许你传递自定义实现,这在想要使用非默认类加载器时尤为重要。

HttpSession with JDBC

你可以通过在使用 `HttpSession`的任何内容之前添加一个 Servlet 过滤器来使用 `HttpSession`的 Spring Session。你可以使用以下任何一种方式:

JDBC Java-based Configuration

本部分介绍了在使用基于 Java 的配置时如何使用关系数据库来支持 HttpSession

HttpSession JDBC Sample 提供了一个关于如何通过 Java 配置整合 Spring Session 和 HttpSession 的工作示例。你可以在接下来的几节中阅读整合的基本步骤,但是我们鼓励你在使用自己的应用程序进行整合时遵循 HttpSession JDBC 指南的具体内容。

@14

JDBC XML-based Configuration

本节介绍了在基于 XML 的配置中使用关系数据库作为 HttpSession 的方法。

HttpSession JDBC XML Sample 提供了一个关于如何通过 XML 配置整合 Spring Session 和 HttpSession 的工作示例。你可以在接下来的几节中阅读整合的基本步骤,但是我们鼓励你在使用自己的应用程序进行整合时遵循 HttpSession JDBC XML 指南的具体内容。

@15

JDBC Spring Boot-based Configuration

本节介绍了在使用 Spring Boot 时如何使用关系数据库作为 HttpSession 的后盾。

HttpSession JDBC Spring Boot Sample 提供了一个关于如何通过 Spring Boot 整合 Spring Session 和 HttpSession 的工作示例。你可以在接下来的几节中阅读整合的基本步骤,但是我们鼓励你在使用自己的应用程序进行整合时遵循 HttpSession JDBC Spring Boot 指南的具体内容。

@16

HttpSession with Hazelcast

使用 `HttpSession`的 Spring Session 可通过在使用 `HttpSession`的任何内容之前添加一个 Servlet 过滤器来启用。

本节介绍了如何使用基于 Java 的配置,使用 Hazelcast 来支持 HttpSession

Hazelcast Spring Sample 提供了一个工作示例,演示如何通过使用 Java 配置整合 Spring Session 和 HttpSession。在接下来的部分中,你可以阅读有关整合的基本步骤,但是我们建议你在与自己的应用程序整合时使用详细的 Hazelcast Spring 指南。

@17

How HttpSession Integration Works

幸运的是,HttpSessionHttpServletRequest(用于获取一个 HttpSession 的 API)都是接口。这意味着我们为每个 API 提供我们自己的实现。

本节介绍了 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 会话的 SessionRepositoryFilter 放在与 HttpSession 交互的任何内容之前非常重要的原因。

HttpSession and RESTful APIs

Spring 会话可以通过让会话在标题中提供来处理 RESTful API。

REST Sample 提供了一个工作示例,演示如何在 REST 应用程序中使用 Spring Session 以支持通过标头进行身份验证。在接下来的部分中,你可以按照所述的基本整合步骤进行操作,但是我们建议你在与自己的应用程序整合时使用详细的 REST 指南。

@18

Using HttpSessionListener

Spring 会话通过将 SessionDestroyedEventSessionCreatedEvent 转换为 HttpSessionEvent,并声明 SessionEventHttpSessionListenerAdapter 来支持 HttpSessionListener。要使用此支持,您需要:

  • 确保您的`SessionRepository`实现支持并配置为触发`SessionDestroyedEvent`和`SessionCreatedEvent`。

  • 将`SessionEventHttpSessionListenerAdapter`配置为 Spring Bean。

  • HttpSessionListener 中注入每个 SessionEventHttpSessionListenerAdapter

如果您使用 Redis 支持,将 enableIndexingAndEvents 设置为 true@EnableRedisHttpSession(enableIndexingAndEvents = true),您所要做的就是将每个 HttpSessionListener 注册为一个 Bean。例如,假设您希望支持 Spring Security 的并发控制,并且需要使用 HttpSessionEventPublisher。在这种情况下,您可以将 HttpSessionEventPublisher 添加为一个 Bean。在 Java 配置中,这可能如下所示:

/*
 * Copyright 2014-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package docs.http;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;

// tag::config[]
@Configuration
@EnableRedisHttpSession
public class RedisHttpSessionConfig {

	@Bean
	public HttpSessionEventPublisher httpSessionEventPublisher() {
		return new HttpSessionEventPublisher();
	}

	// ...

}
// end::config[]

在 XML 配置中,这可能如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util-4.1.xsd">

	<!-- tag::config[] -->
	<bean class="org.springframework.security.web.session.HttpSessionEventPublisher"/>
	<!-- end::config[] -->


	<context:annotation-config/>
	<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisIndexedHttpSessionConfiguration"/>

	<bean class="docs.http.AbstractHttpSessionListenerTests"
		factory-method="createMockRedisConnection"/>
	<bean class="docs.http.AbstractHttpSessionListenerTests$SecuritySessionDestroyedListener"/>
</beans>