This version is still in development and is not considered stable yet. For the latest stable version, please use Spring Security 6.4.1! |
Authorize HttpServletRequests with AuthorizationFilter
This section builds on Servlet Architecture and Implementation by digging deeper into how authorization works within Servlet-based applications.
AuthorizationFilter supersedes FilterSecurityInterceptor .
To remain backward compatible, FilterSecurityInterceptor remains the default.
This section discusses how AuthorizationFilter works and how to override the default configuration.
|
The AuthorizationFilter
provides authorization for HttpServletRequest
s.
It is inserted into the FilterChainProxy as one of the Security Filters.
You can override the default when you declare a SecurityFilterChain
.
Instead of using authorizeRequests
, use authorizeHttpRequests
, like so:
-
Java
@Bean
SecurityFilterChain web(HttpSecurity http) throws AuthenticationException {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated();
)
// ...
return http.build();
}
This improves on authorizeRequests
in a number of ways:
-
Uses the simplified
AuthorizationManager
API instead of metadata sources, config attributes, decision managers, and voters. This simplifies reuse and customization. -
Delays
Authentication
lookup. Instead of the authentication needing to be looked up for every request, it will only look it up in requests where an authorization decision requires authentication. -
Bean-based configuration support.
When authorizeHttpRequests
is used instead of authorizeRequests
, then AuthorizationFilter
is used instead of FilterSecurityInterceptor
.
-
First, the
AuthorizationFilter
obtains an Authentication from the SecurityContextHolder. It wraps this in anSupplier
in order to delay lookup. -
Second, it passes the
Supplier<Authentication>
and theHttpServletRequest
to theAuthorizationManager
.-
If authorization is denied, an
AccessDeniedException
is thrown. In this case theExceptionTranslationFilter
handles theAccessDeniedException
. -
If access is granted,
AuthorizationFilter
continues with the FilterChain which allows the application to process normally.
-
We can configure Spring Security to have different rules by adding more rules in order of precedence.
-
Java
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
http
// ...
.authorizeHttpRequests(authorize -> authorize (1)
.mvcMatchers("/resources/**", "/signup", "/about").permitAll() (2)
.mvcMatchers("/admin/**").hasRole("ADMIN") (3)
.mvcMatchers("/db/**").access((authentication, request) ->
Optional.of(hasRole("ADMIN").check(authentication, request))
.filter((decision) -> !decision.isGranted())
.orElseGet(() -> hasRole("DBA").check(authentication, request));
) (4)
.anyRequest().denyAll() (5)
);
return http.build();
}
1 | There are multiple authorization rules specified. Each rule is considered in the order they were declared. |
2 | We specified multiple URL patterns that any user can access. Specifically, any user can access a request if the URL starts with "/resources/", equals "/signup", or equals "/about". |
3 | Any URL that starts with "/admin/" will be restricted to users who have the role "ROLE_ADMIN".
You will notice that since we are invoking the hasRole method we do not need to specify the "ROLE_" prefix. |
4 | Any URL that starts with "/db/" requires the user to have both "ROLE_ADMIN" and "ROLE_DBA".
You will notice that since we are using the hasRole expression we do not need to specify the "ROLE_" prefix. |
5 | Any URL that has not already been matched on is denied access. This is a good strategy if you do not want to accidentally forget to update your authorization rules. |
You can take a bean-based approach by constructing your own RequestMatcherDelegatingAuthorizationManager
like so:
-
Java
@Bean
SecurityFilterChain web(HttpSecurity http, AuthorizationManager<RequestAuthorizationContext> access)
throws AuthenticationException {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().access(access)
)
// ...
return http.build();
}
@Bean
AuthorizationManager<RequestAuthorizationContext> requestMatcherAuthorizationManager(HandlerMappingIntrospector introspector) {
RequestMatcher permitAll =
new AndRequestMatcher(
new MvcRequestMatcher(introspector, "/resources/**"),
new MvcRequestMatcher(introspector, "/signup"),
new MvcRequestMatcher(introspector, "/about"));
RequestMatcher admin = new MvcRequestMatcher(introspector, "/admin/**");
RequestMatcher db = new MvcRequestMatcher(introspector, "/db/**");
RequestMatcher any = AnyRequestMatcher.INSTANCE;
AuthorizationManager<HttpRequestServlet> manager = RequestMatcherDelegatingAuthorizationManager.builder()
.add(permitAll, (context) -> new AuthorizationDecision(true))
.add(admin, AuthorityAuthorizationManager.hasRole("ADMIN"))
.add(db, AuthorityAuthorizationManager.hasRole("DBA"))
.add(any, new AuthenticatedAuthorizationManager())
.build();
return (context) -> manager.check(context.getRequest());
}
You can also wire your own custom authorization managers for any request matcher.
Here is an example of mapping a custom authorization manager to the my/authorized/endpoint
:
-
Java
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.mvcMatchers("/my/authorized/endpoint").access(new CustomAuthorizationManager());
)
// ...
return http.build();
}
Or you can provide it for all requests as seen below:
-
Java
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest.access(new CustomAuthorizationManager());
)
// ...
return http.build();
}