知識庫 / Spring RSS 訂閱

Spring Session 使用指南

REST,Spring
HongKong
4
03:55 AM · Dec 06 ,2025

1. 概述

Spring Session 的簡單目標是解除 HTTP 會話存儲在服務器上的限制,從而簡化會話管理。

該解決方案使得在雲端之間輕鬆共享會話數據,而無需與單個容器(例如 Tomcat)綁定。此外,它還支持在同一瀏覽器中擁有多個會話以及通過標頭髮送會話。

在本文中,我們將使用 Spring Session 來管理 Web 應用中的身份驗證信息。雖然 Spring Session 可以使用 JDBC、Gemfire 或 MongoDB 存儲數據,但我們將使用 Redis

有關 Redis 的介紹,請查看本文。

2. 一個簡單的項目

讓我們首先創建一個簡單的 Spring Boot 項目,作為我們稍後會使用的示例的基礎:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <relativePath/>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

我們的應用程序使用 Spring Boot,父 pom 文件提供每個條目的版本。每個依賴項的最新版本可以在這裏找到:spring-boot-starter-securityspring-boot-starter-webspring-boot-starter-test

我們還將為我們的 Redis 服務器添加一些配置屬性,位於 application.properties 中:

spring.redis.host=localhost
spring.redis.port=6379

3. Spring Boot 配置

對於 Spring Boot, 只需要添加以下依賴項,自動配置將處理其餘部分:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>

我們使用 boot 父模塊 pom 來設置此處版本,因此這些版本可以保證與我們的其他依賴項兼容。每個依賴項的最新版本可以在這裏找到:spring-boot-starter-data-redisspring-session.

4. 標準 Spring 配置 (不使用 Boot)

讓我們也看看如何集成和配置 spring-session,而不使用 Spring Boot – 僅使用純粹的 Spring。

4.1. 依賴關係

如果我們將 spring-session 添加到標準的 Spring 項目中,則需要明確定義:

<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session</artifactId>
    <version>1.2.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>1.5.0.RELEASE</version>
</dependency>

最新版本的這些模塊可以在這裏找到:spring-sessionspring-data-redis

4.2. Spring Session 配置

現在,讓我們為 Spring Session 添加一個配置類:

@Configuration
@EnableRedisHttpSession
public class SessionConfig extends AbstractHttpSessionApplicationInitializer {
    @Bean
    public JedisConnectionFactory connectionFactory() {
        return new JedisConnectionFactory();
    }
}

@EnableRedisHttpSession以及AbstractHttpSessionApplicationInitializer的擴展將創建一個過濾器,在所有安全基礎設施的前面查找活動會話並從Redis中存儲的值中填充安全上下文。

現在我們來完成這個應用程序,添加一個控制器和安全配置。

5. 應用配置

導航到我們的主應用程序文件並添加一個控制器:

@RestController
public class SessionController {
    @RequestMapping("/")
    public String helloAdmin() {
        return "hello admin";
    }
}

這將使我們獲得一個用於測試的端點。

接下來,添加我們的安全配置類:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public InMemoryUserDetailsManager userDetailsService(PasswordEncoder passwordEncoder) {
        UserDetails user = User.withUsername("admin")
            .password(passwordEncoder.encode("password"))
            .roles("ADMIN")
            .build();
        return new InMemoryUserDetailsManager(user);
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.httpBasic(withDefaults())
            .sessionManagement(httpSecuritySessionManagementConfigurer -> httpSecuritySessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.ALWAYS))
            .authorizeRequests((authorizeRequests) -> authorizeRequests.requestMatchers("/")
                .hasRole("ADMIN")
                .anyRequest()
                .authenticated());
        return http.build();
    }
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

這通過基本身份驗證保護我們的端點,並設置了一個用於測試的用户。

6. 測試

最後,讓我們對所有內容進行測試——我們將定義一個簡單的測試,以便我們能夠完成兩項任務:

  • 消費實時 Web 應用程序
  • 與 Redis 通信

首先,讓我們設置好環境:

public class SessionControllerTest {

    private Jedis jedis;
    private TestRestTemplate testRestTemplate;
    private TestRestTemplate testRestTemplateWithAuth;
    private String testUrl = "http://localhost:8080/";

    @Before
    public void clearRedisData() {
        testRestTemplate = new TestRestTemplate();
        testRestTemplateWithAuth = new TestRestTemplate("admin", "password", null);

        jedis = new Jedis("localhost", 6379);
        jedis.flushAll();
    }
}

請注意我們如何設置這兩個客户端——HTTP 客户端和 Redis 客户端。當然,在此之前,服務器(以及 Redis)應該已經啓動並運行,以便我們通過這些測試與它們進行通信。

我們首先測試 Redis 是否為空:

@Test
public void testRedisIsEmpty() {
    Set<String> result = jedis.keys("*");
    assertEquals(0, result.size());
}

現在測試我們的安全機制是否返回 401 對未身份驗證的請求:

@Test
public void testUnauthenticatedCantAccess() {
    ResponseEntity<String> result = testRestTemplate.getForEntity(testUrl, String.class);
    assertEquals(HttpStatus.UNAUTHORIZED, result.getStatusCode());
}

接下來,我們測試 Spring Session 是否管理我們的身份驗證令牌:

@Test
public void testRedisControlsSession() {
    ResponseEntity<String> result = testRestTemplateWithAuth.getForEntity(testUrl, String.class);
    assertEquals("hello admin", result.getBody()); //login worked

    Set<String> redisResult = jedis.keys("*");
    assertTrue(redisResult.size() > 0); //redis is populated with session data

    String sessionCookie = result.getHeaders().get("Set-Cookie").get(0).split(";")[0];
    HttpHeaders headers = new HttpHeaders();
    headers.add("Cookie", sessionCookie);
    HttpEntity<String> httpEntity = new HttpEntity<>(headers);

    result = testRestTemplate.exchange(testUrl, HttpMethod.GET, httpEntity, String.class);
    assertEquals("hello admin", result.getBody()); //access with session works worked

    jedis.flushAll(); //clear all keys in redis

    result = testRestTemplate.exchange(testUrl, HttpMethod.GET, httpEntity, String.class);
    assertEquals(HttpStatus.UNAUTHORIZED, result.getStatusCode());
    //access denied after sessions are removed in redis
}

首先,我們的測試確認,我們使用管理員認證憑據成功地提交了請求。

然後,我們從響應頭中提取會話值,並將其用作我們第二個請求中的認證。我們驗證了這一點,然後清除所有 Redis 中的數據。

最後,我們使用會話 cookie 再次發起請求,並確認我們已註銷。這證實了 Spring Session 在管理我們的會話。

7. 結論

Spring Session 是一種強大的 HTTP 會話管理工具。 藉助將會話存儲簡化為配置類以及少量 Maven 依賴項,我們現在可以將多個應用程序連接到同一個 Redis 實例,並共享身份驗證信息。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.