Spring Session - 按用户名查找

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

您可以在 findbyusername 应用程序 中找到完整的指南。

假设

本指南假设您已通过使用内置的 Redis 配置支持将 Spring Session 添加到您的应用程序中。本指南还假设您已将 Spring Security 应用于您的应用程序。但是,本指南在某种程度上是通用的,可以应用于任何技术,只需进行最少的更改,我们将在本指南的后面讨论。

如果您需要了解如何将 Spring Session 添加到您的项目中,请参阅 示例和指南 列表

关于示例

我们的示例使用此功能使可能已受到损害的用户会话失效。请考虑以下场景

  • 用户前往图书馆并对应用程序进行身份验证。

  • 用户回家后意识到他们忘记注销了。

  • 用户可以使用位置、创建时间、上次访问时间等线索登录并结束图书馆的会话。

如果我们可以让用户使用他们进行身份验证的任何设备使图书馆的会话失效,那不是很棒吗?本示例演示了这如何成为可能。

使用 FindByIndexNameSessionRepository

要按用户名查找用户,您必须首先选择一个实现 FindByIndexNameSessionRepositorySessionRepository。我们的示例应用程序假设 Redis 支持已设置,因此我们已准备好开始。

映射用户名

仅当开发人员指示 Spring Session 将哪个用户与 Session 关联时,FindByIndexNameSessionRepository 才能按用户名查找会话。您可以通过确保名为 FindByUsernameSessionRepository.PRINCIPAL_NAME_INDEX_NAME 的会话属性填充了用户名来做到这一点。

一般来说,您可以在用户身份验证后立即使用以下代码来实现这一点

String username = "username";
this.session.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username);

使用 Spring Security 映射用户名

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

向会话添加其他数据

将其他信息(如 IP 地址、浏览器、位置和其他详细信息)与会话关联可能很有用。这样做使用户更容易知道他们正在查看哪个会话。

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

public class SessionDetails implements Serializable {

	private String location;

	private String accessType;

	public String getLocation() {
		return this.location;
	}

	public void setLocation(String location) {
		this.location = location;
	}

	public String getAccessType() {
		return this.accessType;
	}

	public void setAccessType(String accessType) {
		this.accessType = accessType;
	}

	private static final long serialVersionUID = 8850489178248613501L;

}

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

@Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
		throws IOException, ServletException {
	chain.doFilter(request, response);

	HttpSession session = request.getSession(false);
	if (session != null) {
		String remoteAddr = getRemoteAddress(request);
		String geoLocation = getGeoLocation(remoteAddr);

		SessionDetails details = new SessionDetails();
		details.setAccessType(request.getHeader("User-Agent"));
		details.setLocation(remoteAddr + " " + geoLocation);

		session.setAttribute("SESSION_DETAILS", details);
	}
}

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

您可能想知道为什么 Spring Session 没有开箱即用地提供 SessionDetails 功能。我们有两个原因。第一个原因是应用程序自己实现这一点非常简单。第二个原因是填充到会话中的信息(以及更新该信息的频率)高度依赖于应用程序。

查找特定用户的会话

我们现在可以查找特定用户的全部会话。以下示例说明了如何做到这一点

@Autowired
FindByIndexNameSessionRepository<? extends Session> sessions;

@RequestMapping("/")
public String index(Principal principal, Model model) {
	Collection<? extends Session> usersSessions = this.sessions.findByPrincipalName(principal.getName()).values();
	model.addAttribute("sessions", usersSessions);
	return "index";
}

在我们的示例中,我们查找当前登录用户的全部会话。但是,您可以对此进行修改,以便管理员使用表单指定要查找的用户。

findbyusername 示例应用程序

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

运行 findbyusername 示例应用程序

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

$ ./gradlew :spring-session-sample-boot-findbyusername:bootRun
为了使示例正常工作,您必须在 localhost 上 安装 Redis 2.8+ 并使用默认端口 (6379) 运行它。或者,您可以更新 RedisConnectionFactory 以指向 Redis 服务器。另一种选择是使用 Docker 在 localhost 上运行 Redis。有关详细说明,请参阅 Docker Redis 存储库

您现在应该可以通过 localhost:8080/ 访问应用程序。

探索安全示例应用程序

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

  • 用户名 user

  • 密码 password

现在点击登录按钮。您应该会看到一条消息,指示您已使用之前输入的用户登录。您还应该看到当前登录用户的活动会话列表。

您可以通过以下步骤模拟我们在关于示例部分中讨论的流程

  • 打开一个新的隐身窗口并导航到 localhost:8080/

  • 输入以下内容登录

    • 用户名 user

    • 密码 password

  • 结束您最初的会话。

  • 刷新原始窗口,您将看到您已注销。