skip to content

What is an AuthenticationEntryPoint, and how does Spring choose one per mechanism when both form login and HTTP Basic are enabled?

level: principalimportance: should knowfreq 45%

answer

  1. Entry point = commence auth for no-credentials request
  2. ExceptionTranslationFilter: AuthException->entryPoint, AccessDenied->handler
  3. form -> LoginUrlAuthEntryPoint (302); basic -> BasicAuthEntryPoint (401 WWW-Authenticate)
  4. multiple mechanisms -> DelegatingAuthenticationEntryPoint by RequestMatcher
  5. cleanest: split SecurityFilterChain per securityMatcher

basics

~20 s

An AuthenticationEntryPoint starts authentication when an unauthenticated request hits a protected resource — e.g. redirect to the login page or send a 401. With multiple mechanisms, Spring uses a DelegatingAuthenticationEntryPoint that picks one via request matchers.

solid answer

~40 s

An AuthenticationEntryPoint is the strategy that 'commences' authentication for a request that arrived without credentials at a protected resource. ExceptionTranslationFilter catches AuthenticationException and calls it. formLogin() contributes LoginUrlAuthenticationEntryPoint (302 to /login); httpBasic() contributes BasicAuthenticationEntryPoint (401 + WWW-Authenticate). When several mechanisms are configured, ExceptionHandlingConfigurer builds a DelegatingAuthenticationEntryPoint holding a map of RequestMatcher to entry point, plus a default. It selects based on matchers — commonly the request's Accept/X-Requested-With headers — so an XHR/API client gets a 401 while a browser gets a redirect to the form. The 'first' configured mechanism generally provides the default. You can override the whole thing with exceptionHandling().authenticationEntryPoint(...) or register your own DelegatingAuthenticationEntryPoint to get precise per-client behavior — critical for apps serving both server-rendered pages and JSON APIs from one filter chain.

code

java · 27 lines
java
// Preferred: two chains, each with its own entry-point behavior
@Bean @Order(1)
SecurityFilterChain apiChain(HttpSecurity http) throws Exception {
    http.securityMatcher("/api/**")
        .authorizeHttpRequests(a -> a.anyRequest().authenticated())
        .httpBasic(Customizer.withDefaults())            // -> 401 + WWW-Authenticate
        .sessionManagement(s -> s.sessionCreationPolicy(STATELESS));
    return http.build();
}

@Bean @Order(2)
SecurityFilterChain webChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests(a -> a.anyRequest().authenticated())
        .formLogin(Customizer.withDefaults());           // -> 302 redirect to /login
    return http.build();
}

// Alternative in ONE chain: explicit delegating entry point
@Bean
AuthenticationEntryPoint delegatingEntryPoint() {
    LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> map = new LinkedHashMap<>();
    map.put(new MediaTypeRequestMatcher(MediaType.APPLICATION_JSON),
            new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)); // JSON -> 401
    DelegatingAuthenticationEntryPoint dp = new DelegatingAuthenticationEntryPoint(map);
    dp.setDefaultEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")); // else redirect
    return dp;
}

go deeper

for a junior

Know an entry point is what redirects you to /login (or returns 401) when you're not logged in.

for a middle

Explain that ExceptionTranslationFilter invokes it and that form vs Basic have different entry points.

for a senior

Describe DelegatingAuthenticationEntryPoint and configuring per-client behavior; distinguish entry point from access-denied handler.

for a principal

Architect mixed HTML+API auth with separate filter chains or precise media-type matchers, and reason about chain ordering and 401/403 correctness.

## What an AuthenticationEntryPoint is An **`AuthenticationEntryPoint`** answers the question: *'A request reached a protected resource but is not authenticated — how do I kick off authentication?'* Its single method is: ``` void commence(HttpServletRequest, HttpServletResponse, AuthenticationException); ``` It does **not** validate credentials; it **initiates** the challenge — redirect to a login form, or return a `401` with a `WWW-Authenticate` header, etc. ## Who calls it The **`ExceptionTranslationFilter`** wraps the rest of the chain. If a downstream filter (usually the `AuthorizationFilter`) throws: - **`AuthenticationException`** → the user is **not authenticated** → call the **`AuthenticationEntryPoint`**. - **`AccessDeniedException`** → the user **is authenticated but lacks authority** → call the **`AccessDeniedHandler`** (typically a 403). This split is fundamental: entry point = 'you need to log in' (401/redirect); access-denied handler = 'you're logged in but not allowed' (403). ## Per-mechanism entry points Each mechanism registers its natural entry point: - **`formLogin()`** → **`LoginUrlAuthenticationEntryPoint`** → HTTP **302** redirect to the login page (also saves the original request in the `RequestCache` so the success handler can return there). - **`httpBasic()`** → **`BasicAuthenticationEntryPoint`** → HTTP **401** with **`WWW-Authenticate: Basic realm="..."`**. - OAuth2/JWT resource server → its own `BearerTokenAuthenticationEntryPoint` (401 with `WWW-Authenticate: Bearer`). ## The selection problem When you enable **more than one** mechanism in a single `SecurityFilterChain`, there can be only one entry point actually invoked for a given unauthenticated request — but you may want **different behavior per client**: browsers should see the login page; API/XHR clients should get a 401 they can handle in JavaScript. Spring's `ExceptionHandlingConfigurer` resolves this at configuration time: 1. It collects the entry points contributed by each configured mechanism, each associated with a **`RequestMatcher`**. 2. If there's exactly one, it uses it directly. 3. If there are several, it wraps them in a **`DelegatingAuthenticationEntryPoint`** — a map of `RequestMatcher → AuthenticationEntryPoint`, plus a **default** entry point for when nothing matches. At runtime it iterates the matchers and delegates to the first match. The matchers are mechanism-specific. HTTP Basic, for instance, registers a matcher favoring requests that look like programmatic clients (e.g. based on the `X-Requested-With` / `Accept` headers), so those get the 401 challenge, while ordinary browser navigations fall through to the form-login redirect as the default. The **order** in which you declare mechanisms influences which becomes the default. ## Taking explicit control Relying on the implicit resolution is fragile for mixed apps. Best practice for an app that serves **both** HTML and JSON from one chain is to either: - **Split into two `SecurityFilterChain` beans** using `securityMatcher` — e.g. `/api/**` with `httpBasic()`/bearer and a 401 entry point, and everything else with `formLogin()`. This is the cleanest and most predictable design. - Or set an explicit entry point: `http.exceptionHandling(e -> e.authenticationEntryPoint(myDelegating))`, where `myDelegating` is a `DelegatingAuthenticationEntryPoint` you build with precise matchers (e.g. `MediaTypeRequestMatcher` for `application/json` → 401; default → redirect). ## Gotchas - Overriding `exceptionHandling().authenticationEntryPoint(...)` sets a **single** entry point for the whole chain, discarding the per-mechanism delegation — fine if you want uniform behavior, surprising if you didn't. - Confusing entry point with **failure handler**: the entry point fires for **no-credentials** requests; the failure handler fires for **rejected login attempts**. They're different filters. - A 401 vs 403 mix-up (unauthenticated vs unauthorized) is a common and meaningful bug for API consumers. - With multiple filter chains, only the **first chain whose `securityMatcher` matches** handles the request — ordering (`@Order`) matters.

  • What is the difference between the AuthenticationEntryPoint and the AccessDeniedHandler?
    Both are invoked by ExceptionTranslationFilter. The entry point handles AuthenticationException — the request is unauthenticated, so it starts auth (redirect to login or 401). The AccessDeniedHandler handles AccessDeniedException — the user is authenticated but lacks the required authority, so it returns 403. 401/redirect vs 403 is the key distinction.
  • How would you serve a browser UI and a JSON API from one app with correct unauthenticated behavior for each?
    Prefer two SecurityFilterChain beans separated by securityMatcher: the /api/** chain uses a 401 entry point (Basic/bearer, stateless), the web chain uses formLogin with a redirect entry point. Alternatively, one chain with a DelegatingAuthenticationEntryPoint keyed on a MediaTypeRequestMatcher for application/json (401) defaulting to the login redirect.

saying these in an interview costs you the question

  • Equating AuthenticationEntryPoint with AuthenticationFailureHandler.
  • Returning 403 (instead of 401/redirect) for unauthenticated requests.
  • Assuming one entry point can serve both browser and API clients without matcher-based delegation or separate chains.

context