Spring Session - 按用户名查找
假设
本指南假设您已经通过使用内置的 Redis 配置支持将 Spring Session 添加到您的应用中。本指南还假设您已经将 Spring Security 应用到您的应用中。然而,本指南具有通用性,只需少量修改即可应用于任何技术,这将在本指南后面讨论。
如果您需要学习如何将 Spring Session 添加到您的项目中,请参阅示例和指南列表 |
关于示例
我们的示例使用此功能来使可能已被泄露的用户会话失效。考虑以下场景
-
用户去图书馆并登录应用程序。
-
用户回家后发现忘记注销了。
-
用户可以登录并使用位置、创建时间、最后访问时间等线索结束图书馆的会话。
如果我们能让用户从任何认证过的设备上使图书馆的会话失效,那岂不是很好?本示例展示了如何实现这一点。
使用 FindByIndexNameSessionRepository
要按用户名查找用户,您首先必须选择一个实现了FindByIndexNameSessionRepository
的 SessionRepository
。我们的示例应用假设 Redis 支持已经设置好,所以我们可以开始了。
映射用户名
只有在开发者指示 Spring Session 哪个用户与 Session
关联时,FindByIndexNameSessionRepository
才能按用户名查找会话。您可以通过确保名为 FindByUsernameSessionRepository.PRINCIPAL_NAME_INDEX_NAME
的会话属性填充了用户名来实现。
一般来说,您可以在用户认证后立即使用以下代码实现:
String username = "username";
this.session.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username);
向会话添加额外数据
将额外信息(例如 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;
}
然后,我们在每个 HTTP 请求中使用 SessionDetailsFilter
将这些信息注入到会话中,如下例所示:
@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/ 访问该应用了。
探索 security 示例应用
现在您可以尝试使用该应用了。输入以下信息登录:
-
用户名 user
-
密码 password
现在点击登录按钮。您应该会看到一条消息,表明您已使用之前输入的用户登录。您还应该会看到当前登录用户的活动会话列表。
您可以通过执行以下操作来模拟我们在关于示例部分讨论的流程:
-
打开一个新的隐身窗口并导航到 localhost:8080/
-
输入以下信息登录:
-
用户名 user
-
密码 password
-
-
结束您的原始会话。
-
刷新原始窗口,您会看到您已注销。