Spring Session - REST

本指南介绍了如何在使用 REST 端点时使用 Spring Session 将 Redis 透明地利用为 Web 应用程序的 HttpSession 提供支持。

This guide describes how to use Spring Session to transparently leverage Redis to back a web application’s HttpSession when you use REST endpoints.

您可以在 rest sample application 中找到已完成的指南。

You can find the completed guide in the rest-sample.

Updating Dependencies

在使用 Spring 会话之前,您必须更新依赖。如果您使用 Maven,您必须添加以下依赖:

Before you use Spring Session, you must update your dependencies. If you use Maven, you must add the following dependencies:

pom.xml
<dependencies>
	<!-- ... -->

	<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>{lettuce-core-version}</version>
	</dependency>
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-web</artifactId>
		<version>{spring-core-version}</version>
	</dependency>
</dependencies>

Spring Configuration

在添加所需的依赖项后,我们可以创建我们的 Spring 配置。Spring 配置负责创建一个 Servlet 过滤器,该过滤器将 HttpSession 实现替换为由 Spring Session 支持的实现。为此,请添加以下 Spring 配置:

After adding the required dependencies, we can create our Spring configuration. The Spring configuration is responsible for creating a servlet filter that replaces the HttpSession implementation with an implementation backed by Spring Session. To do so, add the following Spring Configuration:

Unresolved include directive in modules/ROOT/pages/guides/java-rest.adoc - include::example$spring-session-samples/spring-session-sample-javaconfig-rest/src/main/java/sample/HttpSessionConfig.java[]
1 The @EnableRedisHttpSession annotation creates a Spring bean named springSessionRepositoryFilter that implements Filter. The filter is in charge of replacing the HttpSession implementation to be backed by Spring Session. In this instance, Spring Session is backed by Redis.
2 We create a RedisConnectionFactory that connects Spring Session to the Redis Server. We configure the connection to connect to localhost on the default port (6379). For more information on configuring Spring Data Redis, see the {docs-url}/spring-data/data-redis/docs/{spring-data-redis-version}/reference/html/[reference documentation].
3 We customize Spring Session’s HttpSession integration to use HTTP headers to convey the current session information instead of cookies.

Servlet Container Initialization

我们的 Spring Configuration 创建了一个名为 springSessionRepositoryFilter 的 Spring Bean,它实现了 FilterspringSessionRepositoryFilter Bean 负责使用由 Spring 会话支持的自定义实现来替换 HttpSession

Our rest-spring-configuration created a Spring Bean named springSessionRepositoryFilter that implements Filter. The springSessionRepositoryFilter bean is responsible for replacing the HttpSession with a custom implementation that is backed by Spring Session.

为了让我们的 Filter 发挥作用,Spring 需要加载我们的 Config 类。我们在 Spring MvcInitializer 中提供配置,如下面的示例所示:

In order for our Filter to do its magic, Spring needs to load our Config class. We provide the configuration in our Spring MvcInitializer, as the following example shows:

src/main/java/sample/mvc/MvcInitializer.java
Unresolved include directive in modules/ROOT/pages/guides/java-rest.adoc - include::example$spring-session-samples/spring-session-sample-javaconfig-rest/src/main/java/sample/mvc/MvcInitializer.java[]

最后,我们需要确保我们的 Servlet 容器(即 Tomcat)对每个请求使用我们的 springSessionRepositoryFilter。幸运的是,Spring 会话提供了一个名为 AbstractHttpSessionApplicationInitializer 的实用程序类,使此操作变得简单。为此,使用默认构造函数扩展该类,如下面的示例所示:

Last, we need to ensure that our Servlet Container (that is, Tomcat) uses our springSessionRepositoryFilter for every request. Fortunately, Spring Session provides a utility class named AbstractHttpSessionApplicationInitializer that makes doing so easy. To do so, extend the class with the default constructor, as the following example shows:

src/main/java/sample/Initializer.java
Unresolved include directive in modules/ROOT/pages/guides/java-rest.adoc - include::example$spring-session-samples/spring-session-sample-javaconfig-rest/src/main/java/sample/Initializer.java[]

我们的类 (Initializer) 的名称并不重要,重要的是我们必须延伸`AbstractHttpSessionApplicationInitializer`。

The name of our class (Initializer) does not matter. What is important is that we extend AbstractHttpSessionApplicationInitializer.

rest Sample Application

本部分介绍如何使用 rest 样本应用程序。

This section describes how to use the rest sample application.

Running the rest Sample Application

您可以获取 源代码 并调用以下命令运行示例:

You can run the sample by obtaining the source code and invoking the following command:

要让示例发挥作用,你必须在 localhost 上 install Redis 2.8+ 并使用默认端口 (6379) 运行它。或者,你可以更新 RedisConnectionFactory 以指向 Redis 服务器。另一个选项是使用 Docker 在 localhost 上运行 Redis。有关详细说明,请参见 Docker Redis repository

For the sample to work, you must install Redis 2.8+ on localhost and run it with the default port (6379). Alternatively, you can update the RedisConnectionFactory to point to a Redis server. Another option is to use Docker to run Redis on localhost. See Docker Redis repository for detailed instructions.

$ ./gradlew :spring-session-sample-javaconfig-rest:tomcatRun

您现在应该能够访问 [role="bare"][role="bare"]http://localhost:8080/ 中的应用程序。

You should now be able to access the application at [role="bare"]http://localhost:8080/

Exploring the rest Sample Application

你现在可以尝试使用该应用。要执行此操作,请使用你喜爱的 REST 客户端请求 [role="bare"][role="bare"]http://localhost:8080/。

You can now try to use the application. To do so, use your favorite REST client to request [role="bare"]http://localhost:8080/

	$ curl -v http://localhost:8080/

请注意,系统会提示您进行基本认证。提供以下用户名和密码信息:

Note that you are prompted for basic authentication. Provide the following information for the username and password:

  • Username user

  • Password password

然后,运行以下命令:

Then run the following command:

$ curl -v http://localhost:8080/ -u user:password

在输出中,您应会看到以下内容:

In the output, you should notice the following:

HTTP/1.1 200 OK
...
X-Auth-Token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3

{"username":"user"}

具体来说,您应该注意我们响应的以下内容:

Specifically, you should notice the following things about our response:

  • The HTTP Status is now a 200.

  • We have a header a the name of X-Auth-Token and that contains a new session ID.

  • The current username is displayed.

我们现在可以使用 X-Auth-Token 再次提出请求而无需再次提供用户名和密码。例如,如下命令输出了用户名,与之前一样:

We can now use the X-Auth-Token to make another request without providing the username and password again. For example, the following command outputs the username, as before:

	$ curl -v http://localhost:8080/ -H "X-Auth-Token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3"

唯一的区别是会话 ID 未在响应头中提供,因为我们正在重复使用现有的会话。

The only difference is that the session ID is not provided in the response headers because we are reusing an existing session.

如果我们使会话失效,X-Auth-Token 会显示在响应中,并附带一个空值。例如,如下命令使我们的会话失效:

If we invalidate the session, the X-Auth-Token is displayed in the response with an empty value. For example, the following command invalidates our session:

	$ curl -v http://localhost:8080/logout -H "X-Auth-Token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3"

在输出中,您可以看到 X-Auth-Token 提供了一个空 String,表明上一个会话已失效:

You can see in the output that the X-Auth-Token provides an empty String indicating that the previous session was invalidated:

HTTP/1.1 204 No Content
...
X-Auth-Token:

How Does It Work?

Spring Security 与 SecurityContextPersistenceFilter 中的标准 HttpSession 交互。

Spring Security interacts with the standard HttpSession in SecurityContextPersistenceFilter.

现在,Spring Security 正在持久保留 Redis 中的值,而非使用 Tomcat 的 HttpSession。Spring Session 在浏览器中创建了一个名为 X-Auth-Token 的 header。该 header 包含会话的 ID。

Instead of using Tomcat’s HttpSession, Spring Security is now persisting the values in Redis. Spring Session creates a header named X-Auth-Token in your browser. That header contains the ID of your session.

如果您愿意,您可以轻松地看到会话是在 Redis 中创建的。为此,通过使用如下命令创建会话:

If you like, you can easily see that the session is created in Redis. To do so, create a session by using the following command:

$ curl -v http://localhost:8080/ -u user:password

在输出中,您应会看到以下内容:

In the output, you should notice the following:

HTTP/1.1 200 OK
...
X-Auth-Token: 7e8383a4-082c-4ffe-a4bc-c40fd3363c5e

{"username":"user"}

现在,您可以通过使用 redis-cli 删除会话。例如,在基于 Linux 的系统上,您可以输入:

Now you can remove the session by using redis-cli. For example, on a Linux based system, you can type:

	$ redis-cli keys '*' | xargs redis-cli del

Redis 文档包含有关 installing redis-cli 的说明。

The Redis documentation has instructions for installing redis-cli.

或者,您还可以删除显式密钥。为此,请将以下内容输入到您的终端中,确保将 7e8383a4-082c-4ffe-a4bc-c40fd3363c5e 替换为 SESSION cookie 的值:

Alternatively, you can also delete the explicit key. To do so, enter the following into your terminal, being sure to replace 7e8383a4-082c-4ffe-a4bc-c40fd3363c5e with the value of your SESSION cookie:

	$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e

现在,我们可以使用 X-Auth-Token 对我们已删除的会话发出另一个请求,并观察到我们被提示进行身份验证。例如,如下命令返回一个 HTTP 401:

We can now use the X-Auth-Token to make another request with the session we deleted and observe we that are prompted for authentication. For example, the following returns an HTTP 401:

	$ curl -v http://localhost:8080/ -H "X-Auth-Token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3"