How does the SpEL expression isAnonymous() work, and how does it relate to isAuthenticated() and permitAll()?
answer
- isAnonymous() = not-logged-in placeholder
- complement of isAuthenticated()
- from SecurityExpressionRoot
- anonymous() DSL == access(isAnonymous())
- Thymeleaf sec:authorize supports it
basics
~20 sisAnonymous() is true only when the current user is the anonymous placeholder (not logged in). isAuthenticated() is its opposite for real users. You use these in SpEL, e.g. @PreAuthorize or access() rules, to branch on login state.
solid answer
~40 sisAnonymous() is a built-in Spring Security expression (from SecurityExpressionRoot) that returns true when AuthenticationTrustResolver classifies the current Authentication as an AnonymousAuthenticationToken — i.e., the user is not logged in. isAuthenticated() is essentially its complement: true for any real (non-anonymous, non-remember-me-only depending on config) authentication. isRememberMe() and isFullyAuthenticated() are related refinements. These expressions are usable anywhere Spring evaluates security SpEL: authorizeHttpRequests(...).access(...), @PreAuthorize/@PostAuthorize, and Thymeleaf's sec:authorize. anonymous() in the DSL is the shorthand equivalent of access(isAnonymous()). Note isAnonymous() is about identity state, whereas permitAll() is an unconditional grant; a permitAll endpoint can still call isAnonymous() inside its logic to see whether the caller is logged in. A frequent gotcha: isAnonymous() is false for a fully public request only if that caller is actually authenticated.
code
java · 9 lines// Method security: only guests may call this
@PreAuthorize("isAnonymous()")
public PromoBanner guestPromo() { ... }
// Equivalent authorization-DSL rule
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/promo")
.access(new WebExpressionAuthorizationManager("isAnonymous()"))
.anyRequest().authenticated());go deeper
Know isAnonymous() = not logged in, and it is the opposite of isAuthenticated().
Know where it can be used (SpEL, @PreAuthorize, Thymeleaf) and its DSL equivalence to anonymous().
Explain the trust-resolver backing and the remember-me / fully-authenticated distinctions.
Discuss template/UX branching, disabled-anonymous behavior, and consistency between DSL and method-security expressions.
**What provides it.** Spring Security exposes web/method security expressions via `SecurityExpressionRoot` (and subclasses `WebSecurityExpressionRoot`, `MethodSecurityExpressionRoot`). Built-in expressions include `isAnonymous()`, `isAuthenticated()`, `isFullyAuthenticated()`, `isRememberMe()`, `permitAll`, `denyAll`, `hasRole(...)`, `hasAuthority(...)`. **Semantics of `isAnonymous()`.** Returns `true` iff `AuthenticationTrustResolver.isAnonymous(authentication)` is true — meaning the stored token is an `AnonymousAuthenticationToken`. So it answers 'is this caller the not-logged-in placeholder?' **Relationship to `isAuthenticated()`.** `isAuthenticated()` returns true when the token is neither anonymous nor null. Thus for a normal request one of `isAnonymous()` / `isAuthenticated()` is true and the other false. (Remember-me adds nuance: `isAuthenticated()` is true for remember-me tokens, but `isFullyAuthenticated()` is false — remember-me is still not anonymous.) **Where you use it.** ```java .requestMatchers("/promo").access(new WebExpressionAuthorizationManager("isAnonymous()")) ``` or method security: ```java @PreAuthorize("isAnonymous()") public void onlyForGuests() { ... } ``` or Thymeleaf: `<div sec:authorize="isAnonymous()">Please log in</div>`. **Relationship to the DSL.** `.anonymous()` in `authorizeHttpRequests` is effectively `.access(isAnonymous())`. `.authenticated()` corresponds to `isAuthenticated()`. `.permitAll()` corresponds to `permitAll` (always true) and is unconditional — it does not inspect identity. **Gotchas.** - Don't use `isAnonymous()` to mean 'public'. It is the *inverse* of authenticated; authenticated users fail it. - Inside a `permitAll` endpoint you can still branch: `if (SecurityContextHolder...getAuthentication()` is anonymous) show login CTA. `isAnonymous()` in a template is the clean way to render 'Log in' vs 'My account'. - With anonymous disabled, an unauthenticated request has a `null` Authentication; `isAnonymous()` then behaves as false (no anonymous token), and expression evaluation may treat it as not authenticated.
- How does isAnonymous() differ from isRememberMe() and isFullyAuthenticated()?isAnonymous() is true only for the anonymous placeholder. A remember-me login is NOT anonymous, so isAnonymous() is false, isAuthenticated() is true, but isFullyAuthenticated() is false. isFullyAuthenticated() requires a non-anonymous, non-remember-me authentication.
- Which DSL matcher is equivalent to access("isAnonymous()")?The .anonymous() matcher in authorizeHttpRequests.
saying these in an interview costs you the question
- Saying isAnonymous() means 'public'/everyone
- Claiming isAnonymous() and isAuthenticated() can both be true for the same normal request
- Thinking remember-me users are anonymous