Spring Session - find by username

此指南介绍了如何使用 Spring Session 按用户名查找会话。

This guide describes how to use Spring Session to find sessions by username.

您可以在findbyusername application中找到已完成指南。

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

Assumptions

本指南假设你已使用内置 Redis 配置支持将 Spring Session 添加到你的应用程序中。该指南还假设你已将 Spring Security 应用到你的应用程序中。但是,该指南具有某种通用性,并且只需少量更改即可应用于任何技术,我们将在本指南的后面介绍这一点。

The guide assumes you have already added Spring Session to your application by using the built-in Redis configuration support. The guide also assumes you have already applied Spring Security to your application. However, the guide is somewhat general purpose and can be applied to any technology with minimal changes, which we discuss later in the guide.

如果你需要了解如何将 Spring Session 添加到你的项目中,请参见 samples and guides 的列表。

If you need to learn how to add Spring Session to your project, see the listing of samples and guides

About the Sample

我们的示例使用此功能来使可能遭到入侵的用户的会话失效。考虑以下场景:

Our sample uses this feature to invalidate the users session that might have been compromised. Consider the following scenario:

  • User goes to library and authenticates to the application.

  • User goes home and realizes they forgot to log out.

  • User can log in and end the session from the library using clues like the location, created time, last accessed time, and so on.

使用他们进行身份验证的任何设备让用户在图书馆使会话失效不是很好吗?此示例说明如何实现这一点。

Would it not be nice if we could let the user invalidate the session at the library from any device with which they authenticate? This sample demonstrates how this is possible.

Using FindByIndexNameSessionRepository

要根据用户名查找用户,你必须首先选择一个实现 link:../#api-findbyindexnamesessionrepository[FindByIndexNameSessionRepositorySessionRepository。我们的示例应用程序假设 Redis 支持已设置妥当,因此我们可以开始。

To look up a user by their username, you must first choose a SessionRepository that implements FindByIndexNameSessionRepository. Our sample application assumes that the Redis support is already set up, so we are ready to go.

Mapping the User Name

如果开发人员指示 Spring Session 哪个用户与 Session 关联,则 FindByIndexNameSessionRepository 才能根据用户名查找会话。通过确保名为 FindByUsernameSessionRepository.PRINCIPAL_NAME_INDEX_NAME 的会话属性填充有用户名,你可以完成该操作。

FindByIndexNameSessionRepository can find a session only by the user name if the developer instructs Spring Session what user is associated with the Session. You can do so by ensuring that the session attribute with the name FindByUsernameSessionRepository.PRINCIPAL_NAME_INDEX_NAME is populated with the username.

总的来说,你可以立即在用户进行身份验证后使用以下代码:

Generally speaking, you can do so with the following code immediately after the user authenticates:

/*
 * Copyright 2014-2022 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;

import java.util.Map;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.Session;

/**
 * @author Rob Winch
 *
 */
@ExtendWith(MockitoExtension.class)
class FindByIndexNameSessionRepositoryTests {

	@Mock
	FindByIndexNameSessionRepository<Session> sessionRepository;

	@Mock
	Session session;

	@Test
	void setUsername() {
		// tag::set-username[]
		String username = "username";
		this.session.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username);
		// end::set-username[]
	}

	@Test
	void findByUsername() {
		// tag::findby-username[]
		String username = "username";
		Map<String, Session> sessionIdToSession = this.sessionRepository.findByPrincipalName(username);
		// end::findby-username[]
	}

}

Mapping the User Name with Spring Security

因为我们使用 Spring Security,所以会自动为我们编制用户名索引。这意味着我们无需执行任何步骤来确保对用户名进行编制索引。

Since we use Spring Security, the user name is automatically indexed for us. This means we need not perform any steps to ensure the user name is indexed.

Adding Additional Data to the Session

可以将其他信息(如 IP 地址、浏览器、位置和其他详情)关联到会话,这样可以更方便用户了解他们正在查看哪个会话。

It may be nice to associate additional information (such as the IP Address, the browser, location, and other details) to the session. Doing so makes it easier for the user to know which session they are looking at.

为此,确定您要使用的会话属性以及您希望提供什么信息。然后,创建以会话属性添加的 Java bean。例如,我们的示例应用程序包括会话的位置和访问类型,如下表所示:

To do so, determine which session attribute you want to use and what information you wish to provide. Then create a Java bean that is added as a session attribute. For example, our sample application includes the location and access type of the session, as the following listing shows:

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

我们然后在每个 HTTP 请求中使用 SessionDetailsFilter 将该信息注入会话,如下例所示:

We then inject that information into the session on each HTTP request using a SessionDetailsFilter, as the following example shows:

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

我们获取所需信息,然后将 SessionDetails 设置为 Session 中的一个属性。当我们按用户名检索 Session 时,我们可以使用会话访问我们的 SessionDetails,就像访问任何其他会话属性一样。

We obtain the information we want and then set the SessionDetails as an attribute in the Session. When we retrieve the Session by user name, we can then use the session to access our SessionDetails as we would any other session attribute.

您可能会想,为什么 Spring 会话一开始没有提供 SessionDetails 功能。我们有两个原因。第一个原因是,应用程序自己实现这一点非常简单。第二个原因是在会话中填充的信息(以及该信息的更新频率)在很大程度上取决于应用程序。

You might wonder why Spring Session does not provide SessionDetails functionality out of the box. We have two reasons. The first reason is that it is very trivial for applications to implement this themselves. The second reason is that the information that is populated in the session (and how frequently that information is updated) is highly application-dependent.

Finding sessions for a specific user

现在我们可以找到某个特定用户的所有会话。以下示例展示了如何进行此操作:

We can now find all the sessions for a specific user. The following example shows how to do so:

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

在我们的示例中,我们找到了当前登录用户的会话。但是,您可以修改它,让管理员使用一个表单来指定要查找哪个用户。

In our instance, we find all sessions for the currently logged in user. However, you can modify this for an administrator to use a form to specify which user to look up.

findbyusername Sample Application

本节介绍如何使用 findbyusername 示例应用程序。

This section describes how to use the findbyusername sample application.

Running the findbyusername Sample Application

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

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

$ ./gradlew :spring-session-sample-boot-findbyusername:bootRun

要让示例发挥作用,你必须在 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.

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

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

Exploring the security Sample Application

现在您可以尝试使用该应用程序。输入以下内容来登录:

You can now try using the application. Enter the following to log in:

  • Username user

  • Password password

然后点击 Login 按钮。现在您应该会看到一条消息,指出您已使用先前输入的用户登录。您还应该看到一个针对当前已登录用户的会话列表。

Now click the Login button. You should now see a message indicating your are logged in with the user entered previously. You should also see a listing of active sessions for the currently logged in user.

您可以通过进行以下操作模拟我们在 About the Sample 节中讨论的流程:

You can emulate the flow we discussed in the About the Sample section by doing the following:

  • Open a new incognito window and navigate to [role="bare"]http://localhost:8080/

  • Enter the following to log in:

    • Username user

    • Password password

  • End your original session.

  • Refresh the original window and see that you are logged out.