Spring Session - 按用户名查找

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

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

假设

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

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

关于示例

我们的示例使用此功能来使可能已被泄露的用户会话失效。请考虑以下场景

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

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

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

如果我们可以让用户从他们进行身份验证的任何设备上使图书馆的会话失效,那不是很好吗?本示例演示了如何实现这一点。

使用 FindByIndexNameSessionRepository

要通过用户名查找用户,您必须首先选择一个实现 FindByIndexNameSessionRepositorySessionRepository。我们的示例应用程序假设 Redis 支持已设置,因此我们可以直接开始。

映射用户名

FindByIndexNameSessionRepository 只能通过用户名查找会话,前提是开发人员指示 Spring Session 将哪个用户与 Session 关联。您可以通过确保名为 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
为了使示例正常工作,您必须在本地主机上 安装 Redis 2.8+ 并使用默认端口 (6379) 运行它。或者,您可以更新 RedisConnectionFactory 以指向 Redis 服务器。另一个选择是使用 Docker 在本地主机上运行 Redis。有关详细说明,请参阅 Docker Redis 存储库

您现在应该能够在 localhost:8080/ 访问应用程序。

探索安全示例应用程序

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

  • 用户名 user

  • 密码 password

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

您可以通过执行以下操作来模拟我们在 关于示例 部分中讨论的流程。

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

  • 输入以下内容以登录

    • 用户名 user

    • 密码 password

  • 结束您的原始会话。

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