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;

}

然後,我們在每個 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

  • 結束您的原始會話。

  • 重新整理原始視窗,您會看到您已登出。