Authorization Rules
Authorization Rules
Authentication answers who are you?. Authorization answers what are you allowed to do?. In Spring Security 6, authorization rules for HTTP requests are declared fluently inside SecurityFilterChain using the authorizeHttpRequests DSL. Getting these rules right — and understanding their order, precedence, and security implications — is one of the highest-leverage skills you can build as a Spring developer.
The authorizeHttpRequests DSL
When you register a SecurityFilterChain bean you call http.authorizeHttpRequests(auth -> auth...). Each call inside the lambda adds a request matcher rule. Spring Security evaluates them top-to-bottom and stops at the first match, so rule order is critical.
.anyRequest().authenticated() before your permitAll() rules, every request (including your login page) will require authentication, locking users out permanently. Most security misconfigurations in Spring apps are ordering bugs.
Request Matchers
Spring Security 6 uses requestMatchers() (replacing the deprecated antMatchers() and mvcMatchers()). It accepts Ant-style path patterns, HTTP methods, or combinations of both.
antMatchers(). If you are migrating from Spring Security 5, replace every antMatchers() call with requestMatchers(). The pattern syntax is the same, but the new method also understands Spring MVC route patterns automatically when the MVC dispatcher is on the classpath.
Roles vs Authorities
Spring Security stores both roles and fine-grained authorities as strings on the Authentication object. The distinction is a naming convention: roles are authorities prefixed with ROLE_.
hasRole("ADMIN")— checks for the authorityROLE_ADMIN(the prefix is added automatically).hasAuthority("ROLE_ADMIN")— checks for the exact string you supply. No prefix is added.hasAnyRole("USER", "ADMIN")— passes if the principal has any of the listed roles.hasAnyAuthority("read:orders", "write:orders")— useful for OAuth 2 scopes or fine-grained permissions.
Securing URL Patterns — A Realistic Example
Consider a small e-commerce application with three actor types: anonymous visitors, authenticated customers, and admins. Here is how a complete rule set would look in practice:
Note the use of denyAll() as the catch-all instead of authenticated(). In a well-scoped API this is the safer default: any endpoint not explicitly whitelisted is denied, so newly added endpoints do not accidentally become public before rules are written for them.
denyAll() as your catch-all in REST APIs, and authenticated() for traditional web apps. In a REST service every resource should be explicitly permitted; an implicit catch-all of authenticated() can silently expose endpoints you forgot to protect.
Access Decision Expressions with SpEL
For complex conditions that go beyond a single role check you can use Spring Expression Language (SpEL) via access(). This unlocks IP address checks, time-of-day gating, or combining multiple roles with boolean logic:
Security Implications and Common Pitfalls
- Missing catch-all rule: Without
.anyRequest()at the end, any unmatched URL bypasses your security configuration entirely. Always end with.anyRequest().denyAll()or.anyRequest().authenticated(). - Overly broad wildcards:
/api/**matches/api/admin/usersas well as/api/products. Make sure your broader rules come after your narrower ones. - Relying on URL security alone: If a user with only
ROLE_CUSTOMERsomehow invokes an admin service method directly, URL rules do not protect you. Combine with method-level security (the next lesson) for defence in depth. - Actuator exposure: Spring Boot's
/actuator/**endpoints are notoriously easy to forget. Always explicitly restrict them toROLE_OPSor an IP allowlist.
Summary
Authorization rules in Spring Security 6 are declared with authorizeHttpRequests inside a SecurityFilterChain bean. Rules are matched top-to-bottom — specific paths must come before broad wildcards. Use hasRole() for role-based checks (the ROLE_ prefix is added automatically) and hasAuthority() for exact authority strings such as OAuth 2 scopes. End every rule set with .anyRequest().denyAll() or .anyRequest().authenticated() to prevent accidental exposure of unmatched endpoints. In the next lesson you will push authorization down to the method level using @PreAuthorize and @PostAuthorize.