在本快速教程中,我們將演示如何使用 Spring Security 在應用程序中跟蹤當前已登錄的用户。
為此目的,我們將通過在用户登錄時添加用户並註銷時刪除用户來跟蹤已登錄用户的列表。
我們將利用 HttpSessionBindingListener 在用户登錄系統或註銷系統時,根據用户登錄或註銷情況,更新已登錄用户的列表。
2. 活躍用户存儲
為了簡化,我們將定義一個作為內存中存儲已登錄用户的類:
public class ActiveUserStore {
public List<String> users;
public ActiveUserStore() {
users = new ArrayList<String>();
}
// standard getter and setter
}
我們將將其定義為 Spring 上下文中標準 Bean:
@Bean
public ActiveUserStore activeUserStore(){
return new ActiveUserStore();
}
3. The HTTPSessionBindingListener
現在,我們將利用 HTTPSessionBindingListener 接口並創建一個包裝類來表示當前已登錄的用户。
這將基本上監聽 HttpSessionBindingEvent 事件,這些事件在值設置或刪除、或綁定或取消綁定到 HTTP 會話時觸發:
@Component
public class LoggedUser implements HttpSessionBindingListener, Serializable {
private static final long serialVersionUID = 1L;
private String username;
private ActiveUserStore activeUserStore;
public LoggedUser(String username, ActiveUserStore activeUserStore) {
this.username = username;
this.activeUserStore = activeUserStore;
}
public LoggedUser() {}
@Override
public void valueBound(HttpSessionBindingEvent event) {
List users = activeUserStore.getUsers();
LoggedUser user = (LoggedUser) event.getValue();
if (!users.contains(user.getUsername())) {
users.add(user.getUsername());
}
}
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
List users = activeUserStore.getUsers();
LoggedUser user = (LoggedUser) event.getValue();
if (users.contains(user.getUsername())) {
users.remove(user.getUsername());
}
}
// standard getter and setter
}
監聽器有兩個需要實現的必須方法,valueBound 和 valueUnbound,用於處理觸發事件的兩種類型的操作。每當類型實現監聽器值的設置或刪除、或會話失效時,這兩個方法將被調用。
在本例中,valueBound 方法將在用户登錄時調用,valueUnbound 方法將在用户註銷或會話過期時調用。
在每個方法中,我們檢索與事件關聯的值,然後根據值是否從會話中綁定或取消綁定,將用户名添加到我們的已登錄用户列表中。
4. 跟蹤登錄和登出
現在我們需要跟蹤用户成功登錄或登出時,以便可以從會話中添加或刪除活動用户。在 Spring Security 應用程序中,可以通過實現 AuthenticationSuccessHandler 和 LogoutSuccessHandler 接口來實現。
4.1. 實現 AuthenticationSuccessHandler
對於登錄操作,我們將登錄用户的用户名設置為會話中的一個屬性,通過覆蓋 onAuthenticationSuccess() 方法來獲取會話和 authentication 對象:
@Component("myAuthenticationSuccessHandler")
public class MySimpleUrlAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Autowired
ActiveUserStore activeUserStore;
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException {
HttpSession session = request.getSession(false);
if (session != null) {
LoggedUser user = new LoggedUser(authentication.getName(), activeUserStore);
session.setAttribute("user", user);
}
}
}
4.2. 實現 LogoutSuccessHandler
對於登出操作,我們將移除用户屬性,通過覆蓋 onLogoutSuccess() 方法來實現 LogoutSuccessHandler 接口:
@Component("myLogoutSuccessHandler")
public class MyLogoutSuccessHandler implements LogoutSuccessHandler{
@Override
public void onLogoutSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
HttpSession session = request.getSession();
if (session != null){
session.removeAttribute("user");
}
}
}
5. 控制器和視圖
為了觀察以上所有內容的效果,我們將創建一個控制器映射,用於URL “/users”,它將檢索用户列表,將其作為模型屬性添加到其中,並返回 users.html 視圖:
5.1. 控制器
@Controller
public class UserController {
@Autowired
ActiveUserStore activeUserStore;
@GetMapping("/loggedUsers")
public String getLoggedUsers(Locale locale, Model model) {
model.addAttribute("users", activeUserStore.getUsers());
return "users";
}
}
5.2. users.html
<html>
<body>
<h2>當前已登錄用户</h2>
<div th:each="user : ${users}">
<p th:text="${user}">用户</p>
</div>
</body>
</html>
6. 使用 Sessionregistry 的替代方法
獲取當前已登錄用户的一種方法是利用 Spring 的 Sessionregistry 類,該類管理用户和會話。該類提供 getAllPrincipals() 方法來獲取用户列表。
對於每個用户,我們可以通過調用 getAllSessions() 方法來查看其所有會話列表。為了僅獲取當前已登錄的用户,我們需要排除已過期的會話,通過將 getAllSessions() 方法的第二個參數設置為 false:
@Autowired
private SessionRegistry sessionRegistry;
@Override
public List<String> getUsersFromSessionRegistry() {
return sessionRegistry.getAllPrincipals().stream()
.filter(u -> !sessionRegistry.getAllSessions(u, false).isEmpty())
.map(Object::toString)
.collect(Collectors.toList());
}
為了使用 Sessionregistry 類,我們需要定義 Bean 並將其應用於會話管理,如以下示例所示:
http
.sessionManagement()
.maximumSessions(1).sessionRegistry(sessionRegistry())
...
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
7. 結論
在本文中,我們演示瞭如何在 Spring Security 應用程序中確定當前已登錄的用户。