知識庫 / Spring / Spring Security RSS 訂閱

使用 Spring Security 控制會話

Spring Security
HongKong
6
03:03 PM · Dec 06 ,2025

1. 概述

在本教程中,我們將演示 Spring Security 如何允許我們控制我們的 HTTP 會話。

這種控制範圍從會話超時到啓用併發會話和其他高級安全配置。

2. 會話何時創建?

我們可以精確控制我們的會話何時創建以及 Spring Security 將如何與之交互:

  • always – 如果不存在會話,則會話始終會被創建。
  • ifRequired – 只有在需要時 (default )才會創建會話。
  • never – 框架不會自行創建會話,但如果已存在會話,則會使用它。
  • stateless – Spring Security 不會創建或使用會話。
<http create-session="ifRequired">...</http>

以下是 Java 配置:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.sessionManagement(httpSecuritySessionManagementConfigurer -> 
               httpSecuritySessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
    return http.build();
}

務必理解,

對於更具無狀態性的應用程序, “stateless” ), 是一種 首次引入,並有效地跳過了 Spring Security 過濾器鏈中的部分內容——主要是一些與會話相關的部分,例如 SessionManagementFiltercookies 不會被使用,因此 <listener> <listener-class> org.springframework.security.web.session.HttpSessionEventPublisher </listener-class> </listener>

我們也可以將其定義為 Bean:

@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
    return new HttpSessionEventPublisher();
}

這對於確保 Spring Security 會話註冊表在會話銷燬時被通知至關重要。

為了允許同一用户同時擁有多個會話,應在 XML 配置中<session-management> 元素。

<http ...>
    <session-management>
        <concurrency-control max-sessions="2" />
    </session-management>
</http>

我們也可以通過 Java 配置來完成此操作:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.sessionManagement().maximumSessions(2)
}

5. 會話超時

會話超時是指用户在不活動一段時間後,系統會自動將其會話終止,並將其重新定向到登錄頁面。這是一種安全措施,旨在保護用户帳户免受未經授權的訪問。

5.1. 處理會話超時

如果會話已超時,並且用户發送包含 過期會話 ID 的請求,則用户將被重定向到通過命名空間配置的 URL:

<session-management>
    <concurrency-control expired-url="/sessionExpired.html" ... />
</session-management>

同樣,如果用户發送一個會話 ID 無效但未過期,也會被重定向到可配置的 URL:

<session-management invalid-session-url="/invalidSession.html">
    ...
</session-management>

以下是相應的 Java 配置:

http.sessionManagement(httpSecuritySessionManagementConfigurer -> httpSecuritySessionManagementConfigurer
  .expiredUrl("/sessionExpired")
  .invalidSessionUrl("/invalidSession"));

5.2. 使用 Spring Boot 配置會話超時時間

我們可以通過屬性輕鬆配置嵌入式服務器的會話超時時間:

server.servlet.session.timeout=15m

如果未指定時間單位,Spring 默認假設為秒。

簡而言之,通過這種配置,當會話無操作 15 分鐘後,會話將過期。在此期間,會話將被視為無效。

如果我們的項目配置使用 Tomcat,則需要注意的是,Tomcat 僅支持會話超時一分鐘精度,且最小值為一分鐘。這意味着如果指定超時值為 170s,例如,將會產生兩分鐘的超時。

最後,值得一提的是,雖然 Spring Session 支持類似屬性用於此目的 (spring.session.timeout),但如果未指定該屬性,則會自動回退到我們之前提到的值。

6. 禁用使用 URL 參數進行會話跟蹤

將會話信息暴露在 URL 中是一個日益增長的 安全風險 (自 2007 年第七名,到 2013 年第二名,在 OWASP Top 10 列表中)。

從 Spring 3.0 開始,用於將 jsessionid 追加到 URL 的 URL 重寫邏輯現在可以通過在 &lt;http&gt; 命名空間中設置 disable-url-rewriting=”true” 來禁用。

或者,從 Servlet 3.0 開始,會話跟蹤機制也可以在 web.xml 中配置:

<session-config>
     <tracking-mode>COOKIE</tracking-mode>
</session-config>

以及通過編程方式:

servletContext.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.COOKIE));

它選擇將 JSESSIONID 存儲在 Cookie 中還是 URL 參數中。

7. 使用 Spring Security 防禦 Session 劫持攻擊

該框架通過配置當用户嘗試再次認證時,對現有會話的處理方式,來提供對典型的 Session 劫持攻擊的保護。

<session-management session-fixation-protection="migrateSession"> ...

以下是相應的 Java 配置:

http.sessionManagement(httpSecuritySessionManagementConfigurer -> httpSecuritySessionManagementConfigurer.sessionFixation().migrateSession());

默認情況下,Spring Security 啓用了該保護機制 (“migrateSession”)。 在身份驗證過程中,會創建一個新的 HTTP Session,舊的 Session 被無效化,並且舊 Session 的屬性會被複制到新的 Session 中。

如果這不是我們想要的結果,還有兩個其他的選項:

  • 當“none”設置為 true 時,原始 Session 不會被無效化。
  • 當“newSession”設置為 true 時,會創建一個乾淨的 Session,不會複製任何來自舊 Session 的屬性。

8. <strong>安全會話Cookie</strong>

接下來,我們將討論如何安全地配置我們的會話Cookie。

我們可以將這些標誌設置為我們的會話Cookie,在<em>web.xml</em>中進行配置。

<session-config>
    <session-timeout>1</session-timeout>
    <cookie-config>
        <http-only>true</http-only>
        <secure>true</secure>
    </cookie-config>
</session-config>

此配置選項自 Java Servlet 3 版本開始可用。默認情況下,http-only 為 true,secure 為 false。

下面我們來看對應的 Java 配置:

public class MainWebAppInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext sc) throws ServletException {
        // ...
        sc.getSessionCookieConfig().setHttpOnly(true);        
        sc.getSessionCookieConfig().setSecure(true);        
    }
}

如果使用 Spring Boot,我們可以通過在 application.properties 文件中設置這些標誌:

server.servlet.session.cookie.http-only=true
server.servlet.session.cookie.secure=true

最後,我們還可以手動實現這一點,通過使用過濾器

public class SessionFilter implements Filter {
    @Override
    public void doFilter(
      ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
        Cookie[] allCookies = req.getCookies();
        if (allCookies != null) {
            Cookie session = 
              Arrays.stream(allCookies).filter(x -> x.getName().equals("JSESSIONID"))
                    .findFirst().orElse(null);

            if (session != null) {
                session.setHttpOnly(true);
                session.setSecure(true);
                res.addCookie(session);
            }
        }
        chain.doFilter(req, res);
    }
}

9. Working With the Session

9.1. 基於會話的 Bean

一個 Bean 可以通過在 Web 上下文中聲明 Bean 時使用 @Scope 註解來定義為具有會話範圍。

@Component
@Scope("session")
public class Foo { .. }

或者使用XML:

<bean id="foo" scope="session"/>

然後,這個 Bean 可以注入到另一個 Bean 中:

@Autowired
private Foo theFoo;

Spring 將新的 Bean 綁定到 HTTP Session 的生命週期。

9.2. 將原始會話注入到控制器中

可以使用原始 HTTP 會話直接注入到 Controller 方法中:

@RequestMapping(..)
public void fooMethod(HttpSession session) {
    session.setAttribute(Constants.FOO, new Foo());
    //...
    Foo foo = (Foo) session.getAttribute(Constants.FOO);
}

9.3. 獲取原始會話

當前 HTTP 會話也可以通過 原始 Servlet API 編程方式獲取:

ServletRequestAttributes attr = (ServletRequestAttributes) 
    RequestContextHolder.currentRequestAttributes();
HttpSession session= attr.getRequest().getSession(true); // true == allow create

10. 結論在本文中,我們討論了使用 Spring Security 管理會話。

此外,Spring Reference 包含一份關於會話管理的優秀 常見問題解答

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

發佈 評論

Some HTML is okay.