diff --git a/docs/modules/ROOT/assets/images/servlet/authorization/authorizationfilter.odg b/docs/modules/ROOT/assets/images/servlet/authorization/authorizationfilter.odg new file mode 100644 index 0000000000..5ef95428f9 Binary files /dev/null and b/docs/modules/ROOT/assets/images/servlet/authorization/authorizationfilter.odg differ diff --git a/docs/modules/ROOT/assets/images/servlet/authorization/authorizationfilter.png b/docs/modules/ROOT/assets/images/servlet/authorization/authorizationfilter.png new file mode 100644 index 0000000000..8118785797 Binary files /dev/null and b/docs/modules/ROOT/assets/images/servlet/authorization/authorizationfilter.png differ diff --git a/docs/modules/ROOT/assets/images/servlet/authorization/authorizationhierarchy.odg b/docs/modules/ROOT/assets/images/servlet/authorization/authorizationhierarchy.odg new file mode 100644 index 0000000000..bcfc3c34d4 Binary files /dev/null and b/docs/modules/ROOT/assets/images/servlet/authorization/authorizationhierarchy.odg differ diff --git a/docs/modules/ROOT/assets/images/servlet/authorization/authorizationhierarchy.png b/docs/modules/ROOT/assets/images/servlet/authorization/authorizationhierarchy.png new file mode 100644 index 0000000000..86dc1ac353 Binary files /dev/null and b/docs/modules/ROOT/assets/images/servlet/authorization/authorizationhierarchy.png differ diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 87cca7cc20..f69fee64e6 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -48,7 +48,8 @@ *** xref:servlet/authentication/events.adoc[Authentication Events] ** xref:servlet/authorization/index.adoc[Authorization] *** xref:servlet/authorization/architecture.adoc[Authorization Architecture] -*** xref:servlet/authorization/authorize-requests.adoc[Authorize HTTP Requests] +*** xref:servlet/authorization/authorize-http-requests.adoc[Authorize HTTP Requests] +*** xref:servlet/authorization/authorize-requests.adoc[Authorize HTTP Requests with FilterSecurityInterceptor] *** xref:servlet/authorization/expression-based.adoc[Expression-Based Access Control] *** xref:servlet/authorization/secure-objects.adoc[Secure Object Implementations] *** xref:servlet/authorization/method-security.adoc[Method Security] diff --git a/docs/modules/ROOT/pages/servlet/authorization/architecture.adoc b/docs/modules/ROOT/pages/servlet/authorization/architecture.adoc index dd4ad3ee19..80b8905618 100644 --- a/docs/modules/ROOT/pages/servlet/authorization/architecture.adoc +++ b/docs/modules/ROOT/pages/servlet/authorization/architecture.adoc @@ -8,7 +8,7 @@ == Authorities xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`], discusses how all `Authentication` implementations store a list of `GrantedAuthority` objects. These represent the authorities that have been granted to the principal. -The `GrantedAuthority` objects are inserted into the `Authentication` object by the `AuthenticationManager` and are later read by ``AccessDecisionManager``s when making authorization decisions. +The `GrantedAuthority` objects are inserted into the `Authentication` object by the `AuthenticationManager` and are later read by either the `AuthorizationManager` when making authorization decisions. `GrantedAuthority` is an interface with only one method: @@ -19,25 +19,219 @@ String getAuthority(); ---- -This method allows -``AccessDecisionManager``s to obtain a precise `String` representation of the `GrantedAuthority`. -By returning a representation as a `String`, a `GrantedAuthority` can be easily "read" by most ``AccessDecisionManager``s. +This method allows ``AuthorizationManager``s to obtain a precise `String` representation of the `GrantedAuthority`. +By returning a representation as a `String`, a `GrantedAuthority` can be easily "read" by most ``AuthorizationManager``s and ``AccessDecisionManager``s. If a `GrantedAuthority` cannot be precisely represented as a `String`, the `GrantedAuthority` is considered "complex" and `getAuthority()` must return `null`. An example of a "complex" `GrantedAuthority` would be an implementation that stores a list of operations and authority thresholds that apply to different customer account numbers. Representing this complex `GrantedAuthority` as a `String` would be quite difficult, and as a result the `getAuthority()` method should return `null`. -This will indicate to any `AccessDecisionManager` that it will need to specifically support the `GrantedAuthority` implementation in order to understand its contents. +This will indicate to any `AuthorizationManager` that it will need to specifically support the `GrantedAuthority` implementation in order to understand its contents. Spring Security includes one concrete `GrantedAuthority` implementation, `SimpleGrantedAuthority`. This allows any user-specified `String` to be converted into a `GrantedAuthority`. All ``AuthenticationProvider``s included with the security architecture use `SimpleGrantedAuthority` to populate the `Authentication` object. - [[authz-pre-invocation]] == Pre-Invocation Handling Spring Security provides interceptors which control access to secure objects such as method invocations or web requests. A pre-invocation decision on whether the invocation is allowed to proceed is made by the `AccessDecisionManager`. +=== The AuthorizationManager +`AuthorizationManager` supersedes both <>. + +Applications that customize an `AccessDecisionManager` or `AccessDecisionVoter` are encouraged to <>. + +``AuthorizationManager``s are called by the xref:servlet/authorization/authorize-http-requests.adoc[`AuthorizationFilter`] and are responsible for making final access control decisions. +The `AuthorizationManager` interface contains two methods: + +[source,java] +---- +AuthorizationDecision check(Supplier authentication, Object secureObject); + +default AuthorizationDecision verify(Supplier authentication, Object secureObject) + throws AccessDeniedException { + // ... +} +---- + +The ``AuthorizationManager``'s `check` method is passed all the relevant information it needs in order to make an authorization decision. +In particular, passing the secure `Object` enables those arguments contained in the actual secure object invocation to be inspected. +For example, let's assume the secure object was a `MethodInvocation`. +It would be easy to query the `MethodInvocation` for any `Customer` argument, and then implement some sort of security logic in the `AuthorizationManager` to ensure the principal is permitted to operate on that customer. +Implementations are expected to return a positive `AuthorizationDecision` if access is granted, negative `AuthorizationDecision` if access is denied, and a null `AuthorizationDecision` when abstaining from making a decision. + +`verify` calls `check` and subsequently throws an `AccessDeniedException` in the case of a negative `AuthorizationDecision`. + +[[authz-delegate-authorization-manager]] +=== Delegate-based AuthorizationManager Implementations +Whilst users can implement their own `AuthorizationManager` to control all aspects of authorization, Spring Security ships with a delegating `AuthorizationManager` that can collaborate with individual ``AuthorizationManager``s. + +`RequestMatcherDelegatingAuthorizationManager` will match the request with the most appropriate delegate `AuthorizationManager`. +For method security, you can use `AuthorizationManagerBeforeMethodInterceptor` and `AuthorizationManagerAfterMethodInterceptor`. + +<> illustrates the relevant classes. + +[[authz-authorization-manager-implementations]] +.Authorization Manager Implementations +image::{figures}/authorizationhierarchy.png[] + +Using this approach, a composition of `AuthorizationManager` implementations can be polled on an authorization decision. + +[[authz-authority-authorization-manager]] +==== AuthorityAuthorizationManager +The most common `AuthorizationManager` provided with Spring Security is `AuthorityAuthorizationManager`. +It is configured with a given set of authorities to look for on the current `Authentication`. +It will return positive `AuthorizationDecision` should the `Authentication` contain any of the configured authorities. +It will return a negative `AuthorizationDecision` otherwise. + +[[authz-authenticated-authorization-manager]] +==== AuthenticatedAuthorizationManager +Another manager is the `AuthenticatedAuthorizationManager`. +It can be used to differentiate between anonymous, fully-authenticated and remember-me authenticated users. +Many sites allow certain limited access under remember-me authentication, but require a user to confirm their identity by logging in for full access. + +[[authz-custom-authorization-manager]] +==== Custom Authorization Managers +Obviously, you can also implement a custom `AuthorizationManager` and you can put just about any access-control logic you want in it. +It might be specific to your application (business-logic related) or it might implement some security administration logic. +For example, you can create an implementation that can query Open Policy Agent or your own authorization database. + +[TIP] +You'll find a https://spring.io/blog/2009/01/03/spring-security-customization-part-2-adjusting-secured-session-in-real-time[blog article] on the Spring web site which describes how to use the legacy `AccessDecisionVoter` to deny access in real-time to users whose accounts have been suspended. +You can achieve the same outcome by implementing `AuthorizationManager` instead. + +[[authz-voter-adaptation]] +== Adapting AccessDecisionManager and AccessDecisionVoters + +Previous to `AuthorizationManager`, Spring Security published <>. + +In some cases, like migrating an older application, it may be desirable to introduce an `AuthorizationManager` that invokes an `AccessDecisionManager` or `AccessDecisionVoter`. + +To call an existing `AccessDecisionManager`, you can do: + +.Adapting an AccessDecisionManager +==== +.Java +[source,java,role="primary"] +---- +@Component +public class AccessDecisionManagerAuthorizationManagerAdapter implements AuthorizationManager { + private final AccessDecisionManager accessDecisionManager; + private final SecurityMetadataSource securityMetadataSource; + + @Override + public AuthorizationDecision check(Supplier authentication, Object object) { + try { + Collection attributes = this.securityMetadataSource.getAttributes(object); + this.accessDecisionManager.decide(authentication.get(), object, attributes); + return new AuthorizationDecision(true); + } catch (AccessDeniedException ex) { + return new AuthorizationDecision(false); + } + } + + @Override + public void verify(Supplier authentication, Object object) { + Collection attributes = this.securityMetadataSource.getAttributes(object); + this.accessDecisionManager.decide(authentication.get(), object, attributes); + } +} +---- +==== + +And then wire it into your `SecurityFilterChain`. + +Or to only call an `AccessDecisionVoter`, you can do: + +.Adapting an AccessDecisionVoter +==== +.Java +[source,java,role="primary"] +---- +@Component +public class AccessDecisionVoterAuthorizationManagerAdapter implements AuthorizationManager { + private final AccessDecisionVoter accessDecisionVoter; + private final SecurityMetadataSource securityMetadataSource; + + @Override + public AuthorizationDecision check(Supplier authentication, Object object) { + Collection attributes = this.securityMetadataSource.getAttributes(object); + int decision = this.accessDecisionVoter.vote(authentication.get(), object, attributes); + switch (decision) { + case ACCESS_GRANTED: + return new AuthorizationDecision(true); + case ACCESS_DENIED: + return new AuthorizationDecision(false); + } + return null; + } +} +---- +==== + +And then wire it into your `SecurityFilterChain`. + +[[authz-hierarchical-roles]] +== Hierarchical Roles +It is a common requirement that a particular role in an application should automatically "include" other roles. +For example, in an application which has the concept of an "admin" and a "user" role, you may want an admin to be able to do everything a normal user can. +To achieve this, you can either make sure that all admin users are also assigned the "user" role. +Alternatively, you can modify every access constraint which requires the "user" role to also include the "admin" role. +This can get quite complicated if you have a lot of different roles in your application. + +The use of a role-hierarchy allows you to configure which roles (or authorities) should include others. +An extended version of Spring Security's `RoleVoter`, `RoleHierarchyVoter`, is configured with a `RoleHierarchy`, from which it obtains all the "reachable authorities" which the user is assigned. +A typical configuration might look like this: + +.Hierarchical Roles Configuration +==== +.Java +[source,java,role="primary"] +---- +@Bean +AccessDecisionVoter hierarchyVoter() { + RoleHierarchy hierarchy = new RoleHierarchyImpl(); + hierarchy.setHierarchy("ROLE_ADMIN > ROLE_STAFF\n" + + "ROLE_STAFF > ROLE_USER\n" + + "ROLE_USER > ROLE_GUEST"); + return new RoleHierarcyVoter(hierarchy); +} +---- + +.Xml +[source,java,role="secondary"] +---- + + + + + + + + ROLE_ADMIN > ROLE_STAFF + ROLE_STAFF > ROLE_USER + ROLE_USER > ROLE_GUEST + + + +---- +==== + +Here we have four roles in a hierarchy `ROLE_ADMIN => ROLE_STAFF => ROLE_USER => ROLE_GUEST`. +A user who is authenticated with `ROLE_ADMIN`, will behave as if they have all four roles when security constraints are evaluated against an `AuthorizationManager` adapted to call the above `RoleHierarchyVoter`. +The `>` symbol can be thought of as meaning "includes". + +Role hierarchies offer a convenient means of simplifying the access-control configuration data for your application and/or reducing the number of authorities which you need to assign to a user. +For more complex requirements you may wish to define a logical mapping between the specific access-rights your application requires and the roles that are assigned to users, translating between the two when loading the user information. + +[[authz-legacy-note]] +== Legacy Authorization Components + +[NOTE] +Spring Security contains some legacy components. +Since they are not yet removed, documentation is included for historical purposes. +Their recommended replacements are above. [[authz-access-decision-manager]] === The AccessDecisionManager @@ -72,8 +266,6 @@ Whilst users can implement their own `AccessDecisionManager` to control all aspe .Voting Decision Manager image::{figures}/access-decision-voting.png[] - - Using this approach, a series of `AccessDecisionVoter` implementations are polled on an authorization decision. The `AccessDecisionManager` then decides whether or not to throw an `AccessDeniedException` based on its assessment of the votes. @@ -104,7 +296,6 @@ Like the other implementations, there is a parameter that controls the behaviour It is possible to implement a custom `AccessDecisionManager` that tallies votes differently. For example, votes from a particular `AccessDecisionVoter` might receive additional weighting, whilst a deny vote from a particular voter may have a veto effect. - [[authz-role-voter]] ==== RoleVoter The most commonly used `AccessDecisionVoter` provided with Spring Security is the simple `RoleVoter`, which treats configuration attributes as simple role names and votes to grant access if the user has been assigned that role. @@ -130,14 +321,6 @@ Obviously, you can also implement a custom `AccessDecisionVoter` and you can put It might be specific to your application (business-logic related) or it might implement some security administration logic. For example, you'll find a https://spring.io/blog/2009/01/03/spring-security-customization-part-2-adjusting-secured-session-in-real-time[blog article] on the Spring web site which describes how to use a voter to deny access in real-time to users whose accounts have been suspended. - -[[authz-after-invocation-handling]] -== After Invocation Handling -Whilst the `AccessDecisionManager` is called by the `AbstractSecurityInterceptor` before proceeding with the secure object invocation, some applications need a way of modifying the object actually returned by the secure object invocation. -Whilst you could easily implement your own AOP concern to achieve this, Spring Security provides a convenient hook that has several concrete implementations that integrate with its ACL capabilities. - -<> illustrates Spring Security's `AfterInvocationManager` and its concrete implementations. - [[authz-after-invocation]] .After Invocation Implementation image::{figures}/after-invocation.png[] @@ -151,41 +334,3 @@ If you're using the typical Spring Security included `AccessDecisionManager` imp In turn, if the `AccessDecisionManager` property "`allowIfAllAbstainDecisions`" is `false`, an `AccessDeniedException` will be thrown. You may avoid this potential issue by either (i) setting "`allowIfAllAbstainDecisions`" to `true` (although this is generally not recommended) or (ii) simply ensure that there is at least one configuration attribute that an `AccessDecisionVoter` will vote to grant access for. This latter (recommended) approach is usually achieved through a `ROLE_USER` or `ROLE_AUTHENTICATED` configuration attribute. - - -[[authz-hierarchical-roles]] -== Hierarchical Roles -It is a common requirement that a particular role in an application should automatically "include" other roles. -For example, in an application which has the concept of an "admin" and a "user" role, you may want an admin to be able to do everything a normal user can. -To achieve this, you can either make sure that all admin users are also assigned the "user" role. -Alternatively, you can modify every access constraint which requires the "user" role to also include the "admin" role. -This can get quite complicated if you have a lot of different roles in your application. - -The use of a role-hierarchy allows you to configure which roles (or authorities) should include others. -An extended version of Spring Security's <>, `RoleHierarchyVoter`, is configured with a `RoleHierarchy`, from which it obtains all the "reachable authorities" which the user is assigned. -A typical configuration might look like this: - -[source,xml] ----- - - - - - - - - ROLE_ADMIN > ROLE_STAFF - ROLE_STAFF > ROLE_USER - ROLE_USER > ROLE_GUEST - - - ----- - -Here we have four roles in a hierarchy `ROLE_ADMIN => ROLE_STAFF => ROLE_USER => ROLE_GUEST`. -A user who is authenticated with `ROLE_ADMIN`, will behave as if they have all four roles when security constraints are evaluated against an `AccessDecisionManager` configured with the above `RoleHierarchyVoter`. -The `>` symbol can be thought of as meaning "includes". - -Role hierarchies offer a convenient means of simplifying the access-control configuration data for your application and/or reducing the number of authorities which you need to assign to a user. -For more complex requirements you may wish to define a logical mapping between the specific access-rights your application requires and the roles that are assigned to users, translating between the two when loading the user information. diff --git a/docs/modules/ROOT/pages/servlet/authorization/authorize-http-requests.adoc b/docs/modules/ROOT/pages/servlet/authorization/authorize-http-requests.adoc new file mode 100644 index 0000000000..0b02433caf --- /dev/null +++ b/docs/modules/ROOT/pages/servlet/authorization/authorize-http-requests.adoc @@ -0,0 +1,171 @@ +[[servlet-authorization-authorizationfilter]] += Authorize HttpServletRequests with AuthorizationFilter +:figures: servlet/authorization + +This section builds on xref:servlet/architecture.adoc#servlet-architecture[Servlet Architecture and Implementation] by digging deeper into how xref:servlet/authorization/index.adoc#servlet-authorization[authorization] works within Servlet-based applications. + +[NOTE] +`AuthorizationFilter` supersedes xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`]. +To remain backward compatible, `FilterSecurityInterceptor` remains the default. +This section discusses how `AuthorizationFilter` works and how to override the default configuration. + +The {security-api-url}org/springframework/security/web/access/intercept/AuthorizationFilter.html[`AuthorizationFilter`] provides xref:servlet/authorization/index.adoc#servlet-authorization[authorization] for ``HttpServletRequest``s. +It is inserted into the xref:servlet/architecture.adoc#servlet-filterchainproxy[FilterChainProxy] as one of the xref:servlet/architecture.adoc#servlet-security-filters[Security Filters]. + +You can override the default when you declare a `SecurityFilterChain`. +Instead of using xref:servlet/authorization/authorize-http-requests.adoc#servlet-authorize-requests-defaults[`authorizeRequests`], use `authorizeHttpRequests`, like so: + +.Use authorizeHttpRequests +==== +.Java +[source,java,role="primary"] +---- +@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: + +1. Uses the simplified `AuthorizationManager` API instead of metadata sources, config attributes, decision managers, and voters. +This simplifies reuse and customization. +2. 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. +3. Bean-based configuration support. + +When `authorizeHttpRequests` is used instead of `authorizeRequests`, then {security-api-url}org/springframework/security/web/access/intercept/AuthorizationFilter.html[`AuthorizationFilter`] is used instead of xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`]. + +.Authorize HttpServletRequest +image::{figures}/authorizationfilter.png[] + +* image:{icondir}/number_1.png[] First, the `AuthorizationFilter` obtains an xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[Authentication] from the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder]. +It wraps this in an `Supplier` in order to delay lookup. +* image:{icondir}/number_2.png[] Second, `AuthorizationFilter` creates a {security-api-url}org/springframework/security/web/FilterInvocation.html[`FilterInvocation`] from the `HttpServletRequest`, `HttpServletResponse`, and `FilterChain`. +// FIXME: link to FilterInvocation +* image:{icondir}/number_3.png[] Next, it passes the `Supplier` and `FilterInvocation` to the xref:servlet/architecture.adoc#authz-authorization-manager[`AuthorizationManager`]. +** image:{icondir}/number_4.png[] If authorization is denied, an `AccessDeniedException` is thrown. +In this case the xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] handles the `AccessDeniedException`. +** image:{icondir}/number_5.png[] If access is granted, `AuthorizationFilter` continues with the xref:servlet/architecture.adoc#servlet-filters-review[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. + +.Authorize Requests +==== +.Java +[source,java,role="primary"] +---- +@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("hasRole('ADMIN') and hasRole('DBA')") // <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 xref:servlet/authorization/architecture.adoc#authz-delegate-authorization-manager[`RequestMatcherDelegatingAuthorizationManager`] like so: + +.Configure RequestMatcherDelegatingAuthorizationManager +==== +.Java +[source,java,role="primary"] +---- +@Bean +SecurityFilterChain web(HttpSecurity http, AuthorizationManager access) + throws AuthenticationException { + http + .authorizeHttpRequests((authorize) -> authorize + .anyRequest().access(access) + ) + // ... + + return http.build(); +} + +@Bean +AuthorizationManager 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 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 xref:servlet/authorization/architecture.adoc#authz-custom-authorization-manager[your own custom authorization managers] for any request matcher. + +Here is an example of mapping a custom authorization manager to the `my/authorized/endpoint`: + +.Custom Authorization Manager +==== +.Java +[source,java,role="primary"] +---- +@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: + +.Custom Authorization Manager for All Requests +==== +.Java +[source,java,role="primary"] +---- +@Bean +SecurityFilterChain web(HttpSecurity http) throws Exception { + http + .authorizeHttpRequests((authorize) -> authorize + .anyRequest.access(new CustomAuthorizationManager()); + ) + // ... + + return http.build(); +} +---- +==== diff --git a/docs/modules/ROOT/pages/servlet/authorization/authorize-requests.adoc b/docs/modules/ROOT/pages/servlet/authorization/authorize-requests.adoc index b21c0d096e..57bcea0bf3 100644 --- a/docs/modules/ROOT/pages/servlet/authorization/authorize-requests.adoc +++ b/docs/modules/ROOT/pages/servlet/authorization/authorize-requests.adoc @@ -2,6 +2,10 @@ = Authorize HttpServletRequest with FilterSecurityInterceptor :figures: servlet/authorization +[NOTE] +`FilterSecurityInterceptor` is in the process of being replaced by xref:servlet/authorization/authorize-http-requests.adoc[`AuthorizationFilter`]. +Consider using that instead. + This section builds on xref:servlet/architecture.adoc#servlet-architecture[Servlet Architecture and Implementation] by digging deeper into how xref:servlet/authorization/index.adoc#servlet-authorization[authorization] works within Servlet based applications. The {security-api-url}org/springframework/security/web/access/intercept/FilterSecurityInterceptor.html[`FilterSecurityInterceptor`] provides xref:servlet/authorization/index.adoc#servlet-authorization[authorization] for ``HttpServletRequest``s. @@ -14,7 +18,7 @@ image::{figures}/filtersecurityinterceptor.png[] * image:{icondir}/number_2.png[] Second, `FilterSecurityInterceptor` creates a {security-api-url}org/springframework/security/web/FilterInvocation.html[`FilterInvocation`] from the `HttpServletRequest`, `HttpServletResponse`, and `FilterChain` that are passed into the `FilterSecurityInterceptor`. // FIXME: link to FilterInvocation * image:{icondir}/number_3.png[] Next, it passes the `FilterInvocation` to `SecurityMetadataSource` to get the ``ConfigAttribute``s. -* image:{icondir}/number_4.png[] Finally, it passes the `Authentication`, `FilterInvocation`, and ``ConfigAttribute``s to the `AccessDecisionManager`. +* image:{icondir}/number_4.png[] Finally, it passes the `Authentication`, `FilterInvocation`, and ``ConfigAttribute``s to the xref:servlet/authorization.adoc#authz-access-decision-manager`AccessDecisionManager`. ** image:{icondir}/number_5.png[] If authorization is denied, an `AccessDeniedException` is thrown. In this case the xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] handles the `AccessDeniedException`. ** image:{icondir}/number_6.png[] If access is granted, `FilterSecurityInterceptor` continues with the xref:servlet/architecture.adoc#servlet-filters-review[FilterChain] which allows the application to process normally. @@ -24,6 +28,7 @@ In this case the xref:servlet/architecture.adoc#servlet-exceptiontranslationfilt By default, Spring Security's authorization will require all requests to be authenticated. The explicit configuration looks like: +[[servlet-authorize-requests-defaults]] .Every Request Must be Authenticated ==== .Java @@ -32,7 +37,7 @@ The explicit configuration looks like: protected void configure(HttpSecurity http) throws Exception { http // ... - .authorizeHttpRequests(authorize -> authorize + .authorizeRequests(authorize -> authorize .anyRequest().authenticated() ); } @@ -71,7 +76,7 @@ We can configure Spring Security to have different rules by adding more rules in protected void configure(HttpSecurity http) throws Exception { http // ... - .authorizeHttpRequests(authorize -> authorize // <1> + .authorizeRequests(authorize -> authorize // <1> .mvcMatchers("/resources/**", "/signup", "/about").permitAll() // <2> .mvcMatchers("/admin/**").hasRole("ADMIN") // <3> .mvcMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')") // <4>