Spring Session 指南

REST,Spring
Remote
1
02:37 PM · Dec 01 ,2025

1. 概述

Spring Session 的簡單目標是從服務器端存儲的 HTTP 會話中解放會話管理。

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

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

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

2. 一個簡單的項目

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


    
        org.springframework.boot
        spring-boot-starter-parent
        
    

    
        
            org.springframework.boot
            spring-boot-starter-security
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    

我們的應用程序使用 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 項目中添加 spring-session,則需要明確定義:


<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-session, spring-data-redis

4.2. Spring Session 配置

現在,我們添加一個配置類用於 Spring Session

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

@EnableRedisHttpSessionAbstractHttpSessionApplicationInitializer 的擴展將創建一個過濾器,並在所有安全基礎設施的前面查找活動會話並從 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. 測試

最後,我們來測試一切 – 我們將定義一個簡單的測試,以便我們能夠做 2 件事:

  • 消費實時 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.