Anonymous users and permitAll
Understand why the context is never empty on a permitAll route, and which check proves a real login.
Open this lesson in the learning hubKey points
- An empty context is filled by
AnonymousAuthenticationFilterwith a token holdingROLE_ANONYMOUS. - That token reports
isAuthenticated() == true, so calling it alone never proves that anyone logged in. - Test
!(auth instanceof AnonymousAuthenticationToken), or use SpELisAuthenticated(), which excludes anonymous. permitAll()still runs the whole chain: CSRF, headers and CORS all apply - only the authorization vote is yes.- On a permitAll route
SecurityContextHolderholds the anonymous token, notnull, so a null check passes. - Switching anonymous off leaves a real
nullin the context and turns the trap into a NullPointerException.
Example
http.authorizeHttpRequests(a -> a
.requestMatchers("/", "/health").permitAll()
.anyRequest().authenticated());
// inside a permitAll controller, with nobody logged in
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
auth.getName(); // "anonymousUser"
auth.isAuthenticated(); // true <- the trap
auth.getAuthorities(); // [ROLE_ANONYMOUS]
boolean reallyLoggedIn = !(auth instanceof AnonymousAuthenticationToken);
@PreAuthorize("isAuthenticated()") // SpEL knows the difference: false for anonymous
public List<Order> myOrders() { ... }
On a permitAll route the caller is anonymousUser, not nobody.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Spring Security course, and every lesson in it is listed on the Spring Security contents page.