parent
							
								
									29a4b2bc9b
								
							
						
					
					
						commit
						6b6f473a1b
					
				
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 119 KiB | 
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 121 KiB | 
|  | @ -49,7 +49,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] | ||||
|  |  | |||
|  | @ -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 <<authz-legacy-note,`AccessDecisionManager` and `AccessDecisionVoter`>>. | ||||
| 
 | ||||
| Applications that customize an `AccessDecisionManager` or `AccessDecisionVoter` are encouraged to <<authz-voter-adaptation,change to using `AuthorizationManager`>>. | ||||
| 
 | ||||
| ``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> authentication, Object secureObject); | ||||
| 
 | ||||
| default AuthorizationDecision verify(Supplier<Authentication> 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`. | ||||
| 
 | ||||
| <<authz-authorization-manager-implementations>> 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 <<authz-legacy-note,`AccessDecisionManager` and `AccessDecisionVoter`>>. | ||||
| 
 | ||||
| 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> authentication, Object object) { | ||||
|         try { | ||||
|             Collection<ConfigAttributes> 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> authentication, Object object) { | ||||
|         Collection<ConfigAttributes> 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> authentication, Object object) { | ||||
|         Collection<ConfigAttributes> 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"] | ||||
| ---- | ||||
| 
 | ||||
| <bean id="roleVoter" class="org.springframework.security.access.vote.RoleHierarchyVoter"> | ||||
| 	<constructor-arg ref="roleHierarchy" /> | ||||
| </bean> | ||||
| <bean id="roleHierarchy" | ||||
| 		class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl"> | ||||
| 	<property name="hierarchy"> | ||||
| 		<value> | ||||
| 			ROLE_ADMIN > ROLE_STAFF | ||||
| 			ROLE_STAFF > ROLE_USER | ||||
| 			ROLE_USER > ROLE_GUEST | ||||
| 		</value> | ||||
| 	</property> | ||||
| </bean> | ||||
| ---- | ||||
| ==== | ||||
| 
 | ||||
| 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. | ||||
| 
 | ||||
| <<authz-after-invocation>> 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 <<authz-role-voter,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: | ||||
| 
 | ||||
| [source,xml] | ||||
| ---- | ||||
| 
 | ||||
| <bean id="roleVoter" class="org.springframework.security.access.vote.RoleHierarchyVoter"> | ||||
| 	<constructor-arg ref="roleHierarchy" /> | ||||
| </bean> | ||||
| <bean id="roleHierarchy" | ||||
| 		class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl"> | ||||
| 	<property name="hierarchy"> | ||||
| 		<value> | ||||
| 			ROLE_ADMIN > ROLE_STAFF | ||||
| 			ROLE_STAFF > ROLE_USER | ||||
| 			ROLE_USER > ROLE_GUEST | ||||
| 		</value> | ||||
| 	</property> | ||||
| </bean> | ||||
| ---- | ||||
| 
 | ||||
| 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. | ||||
|  |  | |||
|  | @ -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<Authentication>` 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<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 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(); | ||||
| } | ||||
| ---- | ||||
| ==== | ||||
|  | @ -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> | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue