skip to content

What is the difference between permitAll() and anonymous() in the HttpSecurity authorization DSL?

level: middleimportance: must knowfreq 65%

answer

  1. permitAll = everyone (auth + anon)
  2. anonymous() = only NOT-logged-in
  3. login/register pages -> anonymous()
  4. authenticated user fails anonymous() rule

basics

~20 s

permitAll() lets everyone through, logged-in or not. anonymous() allows only not-logged-in (anonymous) users; an authenticated user hitting an anonymous()-only rule is denied. Use permitAll for public pages, anonymous for login/registration pages you want to hide from signed-in users.

solid answer

~40 s

Both appear in authorizeHttpRequests, but the access rule differs. permitAll() grants access to every request regardless of authentication state — anonymous OR fully authenticated users all pass. anonymous() grants access only when the current Authentication is anonymous (per AuthenticationTrustResolver); a logged-in user is rejected with access-denied. So permitAll is for genuinely public resources (health checks, static assets, public landing pages). anonymous() is for pages that should only be seen when NOT logged in — e.g., a login or registration page you want to redirect authenticated users away from. Because permitAll short-circuits authorization for everyone, the AnonymousAuthenticationFilter still populates the context, but the rule never inspects it. With anonymous(), the rule actively checks that the token is anonymous, which is why authenticated users fail it.

code

java · 10 lines
java
@Bean
SecurityFilterChain chain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests(auth -> auth
        // public: both anonymous and authenticated users allowed
        .requestMatchers("/", "/public/**", "/actuator/health").permitAll()
        // only visible when NOT logged in (login/register)
        .requestMatchers("/login", "/register").anonymous()
        .anyRequest().authenticated());
    return http.build();
}

go deeper

for a junior

Know permitAll = everyone, anonymous = only not-logged-in.

for a middle

Give the login/register use case and explain that authenticated users are denied by anonymous().

for a senior

Tie anonymous() to AuthenticationTrustResolver and note permitAll still runs the rest of the chain.

for a principal

Discuss UX/security implications of hiding auth pages from logged-in users and pitfalls of misusing anonymous() as public.

**Where they live.** Both are terminal matchers in the authorization DSL: ```java http.authorizeHttpRequests(auth -> auth .requestMatchers("/public/**").permitAll() .requestMatchers("/login", "/register").anonymous() .anyRequest().authenticated()); ``` **`permitAll()`** — always grants access, for any caller, authenticated or not. It effectively says 'authorization is a no-op for this matcher'. Both a signed-in user and an anonymous user pass. **`anonymous()`** — grants access *only* when the request's `Authentication` is anonymous, as judged by `AuthenticationTrustResolver.isAnonymous(...)`. A fully authenticated user hitting an `anonymous()`-only rule is **denied** (AccessDeniedException / 403, or a redirect depending on config). It is the DSL equivalent of the SpEL `isAnonymous()`. **Why the distinction exists.** Some pages should be invisible to logged-in users: the login form, the registration form, 'forgot password'. Marking them `anonymous()` means an already-authenticated user cannot reach them and is bounced (often to a landing page). `permitAll()` would let logged-in users see the login page too, which is usually undesirable UX. **Common gotchas.** - People reach for `anonymous()` to mean 'public', but that *breaks* for authenticated users. If both logged-in and logged-out users should see it, use `permitAll()`. - `permitAll()` still runs the rest of the filter chain (CSRF, headers, the anonymous filter) — it only relaxes the authorization decision. - With `permitAll()`, the `SecurityContext` for a logged-out caller is still an `AnonymousAuthenticationToken`, so `isAnonymous()` inside the handler is still true. - Neither disables authentication filters; a valid JWT/session still authenticates the user before the authorization rule is evaluated. **Mental model.** permitAll = 'everyone, don't care who'. anonymous = 'only the not-logged-in'. They are not synonyms.

  • A logged-in user requests a URL mapped with .anonymous(). What happens?
    They are denied — AuthenticationTrustResolver sees a non-anonymous token, so the rule fails, producing an AccessDeniedException (403) or a redirect via the AccessDeniedHandler, since they are already authenticated.
  • If you want a page visible to everyone, which do you use and why?
    permitAll(), because it grants access regardless of authentication state; anonymous() would reject authenticated users.

saying these in an interview costs you the question

  • Treating anonymous() as a synonym for 'public'/permitAll()
  • Believing permitAll() disables the filter chain or the anonymous filter
  • Thinking anonymous() lets authenticated users through

context