Mechanically, how does Spring Session intercept HttpSession usage? Explain the role of SessionRepositoryFilter and how the session id is resolved.
answer
- Bean named springSessionRepositoryFilter
- HIGHEST_PRECEDENCE — before Spring Security filter
- Wraps request so getSession() hits SessionRepository
- HttpSessionIdResolver: Cookie (SESSION, base64) vs Header (X-Auth-Token)
- flushMode ON_SAVE vs IMMEDIATE, write-back on commit
basics
~20 sA servlet filter (SessionRepositoryFilter) runs early and wraps the request. When your code calls getSession(), it returns a session backed by the external store instead of the container's. The session id comes from a cookie via an HttpSessionIdResolver.
solid answer
~40 sThe @Enable...HttpSession annotation registers a bean named springSessionRepositoryFilter — a SessionRepositoryFilter — which Spring Boot wires into the servlet chain at the highest precedence so it runs before anything that touches sessions (including Spring Security). The filter wraps the incoming HttpServletRequest so that request.getSession() returns Spring Session's own HttpSession, loaded from the configured SessionRepository, rather than the container's. On each request it uses an HttpSessionIdResolver to read the id: the default CookieHttpSessionIdResolver reads/writes the SESSION cookie (base64-encoded) via a CookieSerializer; you can swap in HeaderHttpSessionIdResolver to carry the id in an X-Auth-Token header instead (useful for APIs). When the response commits, the filter flushes changes back to the repository and sets the cookie/header. Because the filter is registered highest, downstream filters and controllers transparently see the externalized session.
code
java · 21 lines@Configuration
@EnableRedisHttpSession(flushMode = FlushMode.ON_SAVE)
public class SessionConfig {
// Carry the session id in a header instead of a cookie (API clients):
@Bean
HttpSessionIdResolver httpSessionIdResolver() {
return HeaderHttpSessionIdResolver.xAuthToken();
}
// Or customize the cookie when using the default cookie resolver:
@Bean
CookieSerializer cookieSerializer() {
DefaultCookieSerializer s = new DefaultCookieSerializer();
s.setCookieName("SESSION");
s.setSameSite("Lax");
s.setUseSecureCookie(true);
s.setUseHttpOnlyCookie(true);
return s;
}
}go deeper
Know that a servlet filter wraps the request so getSession() returns an externalized session.
Name SessionRepositoryFilter, its highest-precedence ordering, and the HttpSessionIdResolver cookie-vs-header choice.
Explain the ordering-before-Security requirement, flush modes, and cookie customization via CookieSerializer.
Reason about write-back timing, header-based ids for API gateways, and non-Boot registration via AbstractHttpSessionApplicationInitializer.
## The interception point: SessionRepositoryFilter Spring Session works entirely at the **servlet Filter** layer. When you add `@EnableRedisHttpSession` (or the JDBC/Hazelcast equivalent), the configuration registers a bean **named `springSessionRepositoryFilter`** of type **`SessionRepositoryFilter`**. Spring Boot's `SessionRepositoryFilterConfiguration` registers it into the servlet filter chain with **`Ordered.HIGHEST_PRECEDENCE`** (via `SessionRepositoryFilter.DEFAULT_FILTER_ORDER`), so it executes **before every other filter that might touch the session** — crucially before Spring Security's `springSecurityFilterChain`, which stores the `SecurityContext` in the session. ## Request wrapping On each request the filter wraps the incoming `HttpServletRequest` in a private `SessionRepositoryRequestWrapper` (and the response in a wrapper too). This override changes the behavior of the servlet API methods: - `request.getSession(true)` / `getSession()` → returns Spring Session's own `HttpSession` implementation, whose attributes are loaded from and saved to the **`SessionRepository`** (e.g. `RedisIndexedSessionRepository`, `JdbcIndexedSessionRepository`). - `request.getSession(false)` → looks up the existing session by id in the repository, or returns null. - `session.invalidate()`, `changeSessionId()`, `setAttribute()` → delegated to the repository-backed session. Because the container's own `getSession()` is never reached, the **container never creates its in-memory HttpSession** for these requests. ## Resolving the session id: HttpSessionIdResolver The filter must figure out *which* session id the client is presenting. It delegates to an **`HttpSessionIdResolver`**: - **`CookieHttpSessionIdResolver` (default):** reads and writes the id in a cookie. The cookie is named **`SESSION`** by default (not JSESSIONID) and its value is **base64-encoded** by the `DefaultCookieSerializer`. You customize it with a `CookieSerializer` bean to change the cookie name, `SameSite`, path, `HttpOnly`, secure flag, or to enable **cookie-per-session-id rotation**. - **`HeaderHttpSessionIdResolver`:** carries the id in an HTTP header such as **`X-Auth-Token`**. This is handy for non-browser / API clients that can't manage cookies. You register it by exposing an `HttpSessionIdResolver` bean. ## Write-back timing Spring Session writes the session back to the store and sets the response cookie/header when the response is being committed (the wrapper's `commitSession()` logic). Repositories can support **flush modes** — `ON_SAVE` (default: write once at request end) vs `IMMEDIATE` (write on every attribute change), configurable via the annotation's `flushMode`. `ON_SAVE` minimizes round trips; `IMMEDIATE` reduces the window where an in-flight change could be lost. ## Why order matters (gotcha) If `SessionRepositoryFilter` did **not** run before Spring Security's filter chain, Security would read/write the `SecurityContext` against the container session while Spring Session used a different one, and authentication state would appear to vanish. The highest-precedence ordering is what keeps the two consistent. In a non-Boot setup you register the filter yourself, typically via `AbstractHttpSessionApplicationInitializer`, which maps the delegating filter proxy for `springSessionRepositoryFilter`. ## Summary chain Request → `SessionRepositoryFilter` (highest order) → resolves id via `HttpSessionIdResolver` → wraps request so `getSession()` hits the `SessionRepository` → controllers/Security use it transparently → on commit, session is flushed and the SESSION cookie/header is set.
- Why must the SessionRepositoryFilter run before Spring Security's filter chain?Spring Security stores the SecurityContext (the authenticated principal) in the HttpSession. If the Spring Session filter didn't wrap the request first, Security would read/write the container's session while Spring Session used the externalized one, so login state would be lost. Registering it at HIGHEST_PRECEDENCE guarantees both see the same session.
- How do you make the session id travel in a header instead of a cookie, and when is that useful?Expose an HttpSessionIdResolver bean of type HeaderHttpSessionIdResolver (e.g. HeaderHttpSessionIdResolver.xAuthToken()). It reads/writes the id in the X-Auth-Token header. Useful for native/mobile or API clients that don't manage cookies, and to sidestep CSRF concerns tied to cookies.
saying these in an interview costs you the question
- Saying Spring Session works via an AOP aspect or a HandlerInterceptor rather than a servlet Filter
- Not knowing the filter must precede Spring Security
- Assuming the id can only come from a cookie (ignoring HeaderHttpSessionIdResolver)
- Thinking the container still creates its own HttpSession in parallel