skip to content

How does httpBasic() differ from formLogin(), and what does BasicAuthenticationFilter do?

level: middleimportance: must knowfreq 75%

answer

  1. Basic = Authorization header every request; form = session + cookie
  2. BasicAuthenticationFilter decodes base64(user:pass)
  3. 401 + WWW-Authenticate: Basic realm=...
  4. form login = 302 redirect to /login
  5. base64 != encryption -> require TLS

basics

~20 s

httpBasic() reads credentials from the 'Authorization: Basic' header on every request; formLogin() uses an HTML form and a server session. Basic is stateless and good for APIs/scripts; form login is stateful and good for browsers.

solid answer

~40 s

Both authenticate username/password, but through different mechanisms. formLogin() posts a form once and stores the authenticated user in the session — stateful, browser-friendly, with redirects. httpBasic() registers BasicAuthenticationFilter, which on every request reads the 'Authorization: Basic base64(user:pass)' header, decodes it, builds a UsernamePasswordAuthenticationToken, and calls the AuthenticationManager. Basic is effectively stateless: there's no login page or redirect, and clients (curl, HTTP clients) resend credentials each time. When an unauthenticated request hits a protected resource, Basic's BasicAuthenticationEntryPoint returns 401 with a 'WWW-Authenticate: Basic realm=...' header, prompting the client. Form login instead redirects the browser to the login page via LoginUrlAuthenticationEntryPoint. Basic must run over TLS since credentials are only base64-encoded, not encrypted.

code

java · 15 lines
java
@Bean
SecurityFilterChain api(HttpSecurity http) throws Exception {
    http
        .securityMatcher("/api/**")
        .authorizeHttpRequests(a -> a.anyRequest().authenticated())
        .httpBasic(basic -> basic
            .realmName("KataJob API"))
        // Basic is stateless: don't create sessions for API traffic
        .sessionManagement(s -> s
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
        .csrf(csrf -> csrf.disable()); // safe: no cookies/sessions here
    return http.build();
}
// Client: curl -u alice:secret https://host/api/things
// Header sent: Authorization: Basic YWxpY2U6c2VjcmV0

go deeper

for a junior

Know Basic sends credentials in a header every request while form login uses a session; Basic needs HTTPS.

for a middle

Explain BasicAuthenticationFilter decoding, the 401/WWW-Authenticate challenge vs 302 redirect, and statelessness.

for a senior

Discuss combining both mechanisms, session policy, CSRF implications, and entry-point selection.

for a principal

Weigh Basic vs token auth for service-to-service traffic, credential exposure surface, and rotation/secret-management concerns.

## The two mechanisms side by side Both `formLogin()` and `httpBasic()` authenticate a **username and password**, but the *transport* and *lifecycle* differ. ### formLogin() - Credentials come from **HTML form parameters** in a `POST /login`. - Processed by `UsernamePasswordAuthenticationFilter`. - **Stateful**: the result is stored in the session (`HttpSessionSecurityContextRepository`); the browser then rides a `JSESSIONID` cookie. - Unauthenticated access to a protected page triggers a **redirect** to the login page (`LoginUrlAuthenticationEntryPoint`, HTTP 302). - Meant for **human browser users**. ### httpBasic() - Credentials come from the **`Authorization` HTTP header**: `Authorization: Basic <base64(username:password)>`. - Processed by **`BasicAuthenticationFilter`**. - Effectively **stateless** per request: the client (curl, Postman, another service) re-sends the header every time. There is no login page and no redirect. - Unauthenticated access triggers **`BasicAuthenticationEntryPoint`**, which returns **HTTP 401 Unauthorized** with a **`WWW-Authenticate: Basic realm="..."`** response header. A browser reacts to this by popping up its native credential dialog; a programmatic client reads it and retries with the header. - Meant for **machine-to-machine / API / scripting** access. ## What BasicAuthenticationFilter does, step by step 1. Looks for an `Authorization` header starting with `Basic `. 2. If absent, it does nothing and passes the request down the chain (the request may later be rejected by authorization, triggering the entry point). 3. If present, it **Base64-decodes** the value into `username:password`, splitting on the first colon. 4. Builds an **unauthenticated** `UsernamePasswordAuthenticationToken` and calls the `AuthenticationManager`. 5. On success it stores the authentication in the `SecurityContext` and continues the chain. In Spring Security 6, Basic does **not** persist to the session by default (each request re-authenticates from the header); you can opt into caching. 6. On failure (`AuthenticationException`) it delegates to its configured `AuthenticationEntryPoint` (default `BasicAuthenticationEntryPoint`) to send the 401 challenge, and does **not** continue the chain. ## Security note: base64 is NOT encryption Base64 is trivially reversible. HTTP Basic exposes credentials to anyone who can read the request, so it **must** run over **HTTPS/TLS**. Because credentials travel on every request, the attack surface for interception is larger than a one-time form post. ## Can you enable both? Yes — `http.formLogin(...).httpBasic(...)` registers both filters in the same chain. A browser hitting a page gets redirected to the form; an API client sending the Basic header authenticates directly. When both are on, Spring must decide **which entry point to use** for unauthenticated requests (see the delegating entry point question). ## When to use which - **Form login:** server-rendered web UIs for humans. - **HTTP Basic:** internal service calls, actuator endpoints, quick API auth, CI scripts — always behind TLS. - For public SPAs/mobile APIs, prefer token-based (JWT/OAuth2) over Basic to avoid holding raw credentials client-side. ## Gotchas - CSRF: Basic (stateless, header-based) is not vulnerable to CSRF the way cookie/session auth is; but if you mix session state, keep CSRF on for the browser flow. - Browsers **cache** Basic credentials for the session, making 'logout' awkward. - The realm string is cosmetic but shows in the browser dialog; set it via a custom `BasicAuthenticationEntryPoint`.

  • Why should HTTP Basic always be used over HTTPS?
    The credentials are only Base64-encoded, which is reversible, not encrypted. Over plain HTTP anyone on the path can decode username and password. TLS encrypts the whole request including the Authorization header, and Basic re-sends it on every request, so exposure without TLS is severe.
  • If both httpBasic() and formLogin() are enabled, what happens when an API client without credentials calls a protected URL?
    It depends on which AuthenticationEntryPoint Spring selects. With both mechanisms, a DelegatingAuthenticationEntryPoint chooses based on request matchers/headers (e.g. Accept or X-Requested-With) — ideally returning 401 with WWW-Authenticate for API/XHR clients and a 302 redirect to the form for browsers.

saying these in an interview costs you the question

  • Calling base64 in Basic auth 'encryption'.
  • Saying HTTP Basic is stateful and uses a login page.
  • Believing Basic auth is CSRF-vulnerable the same way cookie/session auth is.

context