Spring Session - 按使用者名稱查詢
假設
本指南假設你已經透過使用內建的 Redis 配置支援將 Spring Session 新增到你的應用程式中。本指南還假設你已經將 Spring Security 應用到你的應用程式中。但是,本指南具有一定的通用性,只需最少的更改即可應用於任何技術,我們將在本指南後面討論。
| 如果你需要學習如何將 Spring Session 新增到你的專案中,請參閱示例和指南列表。 |
關於示例
我們的示例使用此功能來使可能已被洩露的使用者會話失效。考慮以下場景:
-
使用者前往圖書館並嚮應用程式進行身份驗證。
-
使用者回家後,發現忘記登出。
-
使用者可以使用位置、建立時間、上次訪問時間等線索登入並結束圖書館的會話。
如果我們能讓使用者透過他們進行身份驗證的任何裝置使圖書館的會話失效,那豈不是很好?本示例演示瞭如何實現這一點。
使用 FindByIndexNameSessionRepository
要透過使用者名稱查詢使用者,你必須首先選擇一個實現FindByIndexNameSessionRepository的 SessionRepository。我們的示例應用程式假設 Redis 支援已經設定好,因此我們已準備就緒。
對映使用者名稱
FindByIndexNameSessionRepository 只有在開發人員指示 Spring Session 哪個使用者與 Session 關聯時,才能透過使用者名稱查詢會話。你可以透過確保名為 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;
}
然後,我們使用 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
-
-
結束你的原始會話。
-
重新整理原始視窗,你會看到你已登出。