Revisit Request and Method Security Docs

Issue gh-13088
This commit is contained in:
Josh Cummings 2023-04-18 18:13:50 -06:00
parent 42cd19fcee
commit e5fcf1ebcf
No known key found for this signature in database
GPG Key ID: A306A51F43B8E5A5
15 changed files with 2079 additions and 1340 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

View File

@ -59,9 +59,7 @@
** xref:servlet/authorization/index.adoc[Authorization] ** xref:servlet/authorization/index.adoc[Authorization]
*** xref:servlet/authorization/architecture.adoc[Authorization Architecture] *** xref:servlet/authorization/architecture.adoc[Authorization Architecture]
*** xref:servlet/authorization/authorize-http-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/expression-based.adoc[Expression-Based Access Control]
*** xref:servlet/authorization/secure-objects.adoc[Secure Object Implementations]
*** xref:servlet/authorization/method-security.adoc[Method Security] *** xref:servlet/authorization/method-security.adoc[Method Security]
*** xref:servlet/authorization/acls.adoc[Domain Object Security ACLs] *** xref:servlet/authorization/acls.adoc[Domain Object Security ACLs]
*** xref:servlet/authorization/events.adoc[Authorization Events] *** xref:servlet/authorization/events.adoc[Authorization Events]

View File

@ -36,7 +36,7 @@ There are no further migrations steps for Java or Kotlin for this feature.
== Use `AuthorizationManager` for Request Security == Use `AuthorizationManager` for Request Security
In 6.0, `<http>` defaults `once-per-request` to `false`, `filter-all-dispatcher-types` to `true`, and `use-authorization-manager` to `true`. In 6.0, `<http>` defaults `once-per-request` to `false`, `filter-all-dispatcher-types` to `true`, and `use-authorization-manager` to `true`.
Also, xref:servlet/authorization/authorize-requests.adoc#filtersecurityinterceptor-every-request[`authorizeRequests#filterSecurityInterceptorOncePerRequest`] defaults to `false` and xref:servlet/authorization/authorize-http-requests.adoc[`authorizeHttpRequests#filterAllDispatcherTypes`] defaults to `true`. Also, {security-api-url}org/springframework/security/config/annotation/web/configurers/AbstractInterceptUrlConfigurer.AbstractInterceptUrlRegistry.html#filterSecurityInterceptorOncePerRequest(boolean)[`authorizeRequests#filterSecurityInterceptorOncePerRequest`] defaults to `false` and xref:servlet/authorization/authorize-http-requests.adoc[`authorizeHttpRequests#filterAllDispatcherTypes`] defaults to `true`.
So, to complete migration, any defaults values can be removed. So, to complete migration, any defaults values can be removed.
For example, if you opted in to the 6.0 default for `filter-all-dispatcher-types` or `authorizeHttpRequests#filterAllDispatcherTypes` like so: For example, if you opted in to the 6.0 default for `filter-all-dispatcher-types` or `authorizeHttpRequests#filterAllDispatcherTypes` like so:

View File

@ -194,7 +194,7 @@ The following is a comprehensive list of Spring Security Filter ordering:
* `OAuth2AuthorizationCodeGrantFilter` * `OAuth2AuthorizationCodeGrantFilter`
* `SessionManagementFilter` * `SessionManagementFilter`
* <<servlet-exceptiontranslationfilter,`ExceptionTranslationFilter`>> * <<servlet-exceptiontranslationfilter,`ExceptionTranslationFilter`>>
* xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] * xref:servlet/authorization/authorize-http-requests.adoc[`AuthorizationFilter`]
* `SwitchUserFilter` * `SwitchUserFilter`
[[servlet-exceptiontranslationfilter]] [[servlet-exceptiontranslationfilter]]

View File

@ -15,7 +15,7 @@ The preceding figure builds off our xref:servlet/architecture.adoc#servlet-secur
image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the resource `/private` for which it is not authorized. image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the resource `/private` for which it is not authorized.
image:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`. image:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-http-requests.adoc[`AuthorizationFilter`] indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`.
image:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__. image:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__.
The configured xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of {security-api-url}org/springframework/security/web/authentication/www/BasicAuthenticationEntryPoint.html[`BasicAuthenticationEntryPoint`], which sends a WWW-Authenticate header. The configured xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of {security-api-url}org/springframework/security/web/authentication/www/BasicAuthenticationEntryPoint.html[`BasicAuthenticationEntryPoint`], which sends a WWW-Authenticate header.

View File

@ -16,7 +16,7 @@ The preceding figure builds off our xref:servlet/architecture.adoc#servlet-secur
image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the resource (`/private`) for which it is not authorized. image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the resource (`/private`) for which it is not authorized.
image:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`. image:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-http-requests.adoc[`AuthorizationFilter`] indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`.
image:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__ and sends a redirect to the login page with the configured xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`]. image:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__ and sends a redirect to the login page with the configured xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`].
In most cases, the `AuthenticationEntryPoint` is an instance of {security-api-url}org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.html[`LoginUrlAuthenticationEntryPoint`]. In most cases, the `AuthenticationEntryPoint` is an instance of {security-api-url}org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.html[`LoginUrlAuthenticationEntryPoint`].

View File

@ -23,30 +23,76 @@ String getAuthority();
---- ----
==== ====
This method lets an This method is used by an
`AccessDecisionManager` instance to obtain a precise `String` representation of the `GrantedAuthority`. `AuthorizationManager` instance 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` implementations. By returning a representation as a `String`, a `GrantedAuthority` can be easily "read" by most `AuthorizationManager` implementations.
If a `GrantedAuthority` cannot be precisely represented as a `String`, the `GrantedAuthority` is considered "`complex`" and `getAuthority()` must return `null`. 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. 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. As a result, the `getAuthority()` method should return `null`. Representing this complex `GrantedAuthority` as a `String` would be quite difficult. As a result, the `getAuthority()` method should return `null`.
This indicates to any `AccessDecisionManager` that it needs to support the specific `GrantedAuthority` implementation to understand its contents. This indicates to any `AuthorizationManager` that it needs to support the specific `GrantedAuthority` implementation to understand its contents.
Spring Security includes one concrete `GrantedAuthority` implementation: `SimpleGrantedAuthority`. Spring Security includes one concrete `GrantedAuthority` implementation: `SimpleGrantedAuthority`.
This implementation lets any user-specified `String` be converted into a `GrantedAuthority`. This implementation lets any user-specified `String` be converted into a `GrantedAuthority`.
All `AuthenticationProvider` instances included with the security architecture use `SimpleGrantedAuthority` to populate the `Authentication` object. All `AuthenticationProvider` instances included with the security architecture use `SimpleGrantedAuthority` to populate the `Authentication` object.
[[jc-method-security-custom-granted-authority-defaults]]
By default, role-based authorization rules include `ROLE_` as a prefix.
This means that if there is an authorization rule that requires a security context to have a role of "USER", Spring Security will by default look for a `GrantedAuthority#getAuthority` that returns "ROLE_USER".
You can customize this with `GrantedAuthorityDefaults`.
`GrantedAuthorityDefaults` exists to allow customizing the prefix to use for role-based authorization rules.
You can configure the authorization rules to use a different prefix by exposing a `GrantedAuthorityDefaults` bean, like so:
.Custom MethodSecurityExpressionHandler
====
.Java
[source,java,role="primary"]
----
@Bean
static GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults("MYPREFIX_");
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
companion object {
@Bean
fun grantedAuthorityDefaults() : GrantedAuthorityDefaults {
return GrantedAuthorityDefaults("MYPREFIX_");
}
}
----
.Xml
[source,xml,role="secondary"]
----
<bean id="grantedAuthorityDefaults" class="org.springframework.security.config.core.GrantedAuthorityDefaults">
<constructor-arg value="MYPREFIX_"/>
</bean>
----
====
[TIP]
====
You expose `GrantedAuthorityDefaults` using a `static` method to ensure that Spring publishes it before it initializes Spring Security's method security `@Configuration` classes
====
[[authz-pre-invocation]] [[authz-pre-invocation]]
== Pre-Invocation Handling == Invocation Handling
Spring Security provides interceptors that control access to secure objects, such as method invocations or web requests. Spring Security provides interceptors that 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`. A pre-invocation decision on whether the invocation is allowed to proceed is made by `AuthorizationManager` instances.
Also post-invocation decisions on whether a given value may be returned is made by `AuthorizationManager` instances.
=== The AuthorizationManager === The AuthorizationManager
`AuthorizationManager` supersedes both <<authz-legacy-note,`AccessDecisionManager` and `AccessDecisionVoter`>>. `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`>>. 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. ``AuthorizationManager``s are called by Spring Security's xref:servlet/authorization/authorize-http-requests.adoc[request-based], xref:servlet/authorization/method-security.adoc[method-based], and xref:servlet/integrations/websocket.adoc[message-based] authorization components and are responsible for making final access control decisions.
The `AuthorizationManager` interface contains two methods: The `AuthorizationManager` interface contains two methods:
==== ====
@ -97,6 +143,10 @@ Another manager is the `AuthenticatedAuthorizationManager`.
It can be used to differentiate between anonymous, fully-authenticated and remember-me authenticated users. 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. 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-authorization-managers]]
==== AuthorizationManagers
There are also helpful static factories in `AuthenticationManagers` for composing individual ``AuthenticationManager``s into more sophisticated expressions.
[[authz-custom-authorization-manager]] [[authz-custom-authorization-manager]]
==== Custom Authorization Managers ==== 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. Obviously, you can also implement a custom `AuthorizationManager` and you can put just about any access-control logic you want in it.

View File

@ -1,183 +0,0 @@
[[servlet-authorization-filtersecurityinterceptor]]
= 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` instances.
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].
The following image shows the role of `FilterSecurityInterceptor`:
.Authorize HttpServletRequest
image::{figures}/filtersecurityinterceptor.png[]
image:{icondir}/number_1.png[] The `FilterSecurityInterceptor` obtains an xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[Authentication] from the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder].
image:{icondir}/number_2.png[] `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`.
image:{icondir}/number_3.png[] It passes the `FilterInvocation` to `SecurityMetadataSource` to get the ``ConfigAttribute``s.
image:{icondir}/number_4.png[] It passes the `Authentication`, `FilterInvocation`, and ``ConfigAttribute``s to the `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 lets the application process normally.
// configuration (xml/java)
By default, Spring Security's authorization requires all requests to be authenticated.
The following listing shows the explicit configuration:
[[servlet-authorize-requests-defaults]]
.Every Request Must be Authenticated
====
.Java
[source,java,role="primary"]
----
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.authorizeRequests(authorize -> authorize
.anyRequest().authenticated()
);
return http.build();
}
----
.XML
[source,xml,role="secondary"]
----
<http>
<!-- ... -->
<intercept-url pattern="/**" access="authenticated"/>
</http>
----
.Kotlin
[source,kotlin,role="secondary"]
----
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
authorizeRequests {
authorize(anyRequest, authenticated)
}
}
return http.build()
}
----
====
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
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.authorizeRequests(authorize -> authorize // <1>
.requestMatchers("/resources/**", "/signup", "/about").permitAll() // <2>
.requestMatchers("/admin/**").hasRole("ADMIN") // <3>
.requestMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')") // <4>
.anyRequest().denyAll() // <5>
);
return http.build();
}
----
.XML
[source,xml,role="secondary"]
----
<http> <!--1-->
<!-- ... -->
<!--2-->
<intercept-url pattern="/resources/**" access="permitAll"/>
<intercept-url pattern="/signup" access="permitAll"/>
<intercept-url pattern="/about" access="permitAll"/>
<intercept-url pattern="/admin/**" access="hasRole('ADMIN')"/> <!--3-->
<intercept-url pattern="/db/**" access="hasRole('ADMIN') and hasRole('DBA')"/> <!--4-->
<intercept-url pattern="/**" access="denyAll"/> <!--5-->
</http>
----
.Kotlin
[source,kotlin,role="secondary"]
----
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeRequests { // <1>
authorize("/resources/**", permitAll) // <2>
authorize("/signup", permitAll)
authorize("/about", permitAll)
authorize("/admin/**", hasRole("ADMIN")) // <3>
authorize("/db/**", "hasRole('ADMIN') and hasRole('DBA')") // <4>
authorize(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.
====
[[filtersecurityinterceptor-every-request]]
== Configure FilterSecurityInterceptor with Dispatcher Types
By default, the `FilterSecurityInterceptor` applies to every request.
This means that if a request is dispatched from a request that was already filtered, the `FilterSecurityInterceptor` will perform the same authorization checks on the dispatched request.
In some scenarios, you may not want to apply authorization on some dispatcher types:
.Permit ASYNC and ERROR dispatcher types
====
.Java
[source,java,role="primary"]
----
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
http
.authorizeRequests((authorize) -> authorize
.dispatcherTypeMatchers(DispatcherType.ASYNC, DispatcherType.ERROR).permitAll()
.anyRequest.authenticated()
)
// ...
return http.build();
}
----
.XML
[source,xml]
----
<http auto-config="true">
<intercept-url request-matcher-ref="dispatcherTypeMatcher" access="permitAll" />
<intercept-url pattern="/**" access="authenticated"/>
</http>
<b:bean id="dispatcherTypeMatcher" class="org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher">
<b:constructor-arg value="ASYNC"/>
<b:constructor-arg value="ERROR"/>
</b:bean>
----
====

View File

@ -2,11 +2,12 @@
= Authorization = Authorization
:page-section-summary-toc: 1 :page-section-summary-toc: 1
Having established xref:servlet/authentication/index.adoc[how users will authenticate], you also need to configure your application's authorization rules.
The advanced authorization capabilities within Spring Security represent one of the most compelling reasons for its popularity. The advanced authorization capabilities within Spring Security represent one of the most compelling reasons for its popularity.
Irrespective of how you choose to authenticate (whether using a Spring Security-provided mechanism and provider or integrating with a container or other non-Spring Security authentication authority), the authorization services can be used within your application in a consistent and simple way. Irrespective of how you choose to authenticate (whether using a Spring Security-provided mechanism and provider or integrating with a container or other non-Spring Security authentication authority), the authorization services can be used within your application in a consistent and simple way.
In this part, we explore the different `AbstractSecurityInterceptor` implementations, which were introduced in Part I. You should consider attaching authorization rules to xref:servlet/authorization/authorize-http-requests.adoc[request URIs] and xref:servlet/authorization/method-security.adoc[methods] to begin.
We then move on to explore how to fine-tune authorization through the use of domain access control lists. Below there is also wealth of detail about xref:servlet/authorization/architecture.adoc[how Spring Security authorization works] and how, having established a basic model, it can be fine-tuned.

View File

@ -1,143 +0,0 @@
[[secure-object-impls]]
= Secure Object Implementations
This section covers how Spring Security handles Secure Object implementations.
[[aop-alliance]]
== AOP Alliance (MethodInvocation) Security Interceptor
Prior to Spring Security 2.0, securing `MethodInvocation` instances needed a lot of boiler plate configuration.
Now the recommended approach for method security is to use xref:servlet/configuration/xml-namespace.adoc#ns-method-security[namespace configuration].
This way, the method security infrastructure beans are configured automatically for you, so you need not know about the implementation classes.
We provide only a quick overview of the classes that are involved here.
Method security is enforced by using a `MethodSecurityInterceptor`, which secures `MethodInvocation` instances.
Depending on the configuration approach, an interceptor may be specific to a single bean or shared between multiple beans.
The interceptor uses a `MethodSecurityMetadataSource` instance to obtain the configuration attributes that apply to a particular method invocation.
`MapBasedMethodSecurityMetadataSource` is used to store configuration attributes keyed by method names (which can be wildcarded) and will be used internally when the attributes are defined in the application context using the `<intercept-methods>` or `<protect-point>` elements.
Other implementations are used to handle annotation-based configuration.
=== Explicit MethodSecurityInterceptor Configuration
You can configure a `MethodSecurityInterceptor` directly in your application context for use with one of Spring AOP's proxying mechanisms:
====
[source,xml]
----
<bean id="bankManagerSecurity" class=
"org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="afterInvocationManager" ref="afterInvocationManager"/>
<property name="securityMetadataSource">
<sec:method-security-metadata-source>
<sec:protect method="com.mycompany.BankManager.delete*" access="ROLE_SUPERVISOR"/>
<sec:protect method="com.mycompany.BankManager.getBalance" access="ROLE_TELLER,ROLE_SUPERVISOR"/>
</sec:method-security-metadata-source>
</property>
</bean>
----
====
[[aspectj]]
== AspectJ (JoinPoint) Security Interceptor
The AspectJ security interceptor is very similar to the AOP Alliance security interceptor discussed in the previous section.
We discuss only the differences in this section.
The AspectJ interceptor is named `AspectJSecurityInterceptor`.
Unlike the AOP Alliance security interceptor, which relies on the Spring application context to weave in the security interceptor through proxying, the `AspectJSecurityInterceptor` is woven in through the AspectJ compiler.
It would not be uncommon to use both types of security interceptors in the same application, with `AspectJSecurityInterceptor` being used for domain object instance security and the AOP Alliance `MethodSecurityInterceptor` being used for services layer security.
We first consider how the `AspectJSecurityInterceptor` is configured in the Spring application context:
====
[source,xml]
----
<bean id="bankManagerSecurity" class=
"org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="afterInvocationManager" ref="afterInvocationManager"/>
<property name="securityMetadataSource">
<sec:method-security-metadata-source>
<sec:protect method="com.mycompany.BankManager.delete*" access="ROLE_SUPERVISOR"/>
<sec:protect method="com.mycompany.BankManager.getBalance" access="ROLE_TELLER,ROLE_SUPERVISOR"/>
</sec:method-security-metadata-source>
</property>
</bean>
----
====
The two interceptors can share the same `securityMetadataSource`, as the `SecurityMetadataSource` works with `java.lang.reflect.Method` instances rather than an AOP library-specific class.
Your access decisions have access to the relevant AOP library-specific invocation (`MethodInvocation` or `JoinPoint`) and can consider a range of additional criteria (such as method arguments) when making access decisions.
Next, you need to define an AspectJ `aspect`, as the following example shows:
====
[source,java]
----
package org.springframework.security.samples.aspectj;
import org.springframework.security.access.intercept.aspectj.AspectJSecurityInterceptor;
import org.springframework.security.access.intercept.aspectj.AspectJCallback;
import org.springframework.beans.factory.InitializingBean;
public aspect DomainObjectInstanceSecurityAspect implements InitializingBean {
private AspectJSecurityInterceptor securityInterceptor;
pointcut domainObjectInstanceExecution(): target(PersistableEntity)
&& execution(public * *(..)) && !within(DomainObjectInstanceSecurityAspect);
Object around(): domainObjectInstanceExecution() {
if (this.securityInterceptor == null) {
return proceed();
}
AspectJCallback callback = new AspectJCallback() {
public Object proceedWithObject() {
return proceed();
}
};
return this.securityInterceptor.invoke(thisJoinPoint, callback);
}
public AspectJSecurityInterceptor getSecurityInterceptor() {
return securityInterceptor;
}
public void setSecurityInterceptor(AspectJSecurityInterceptor securityInterceptor) {
this.securityInterceptor = securityInterceptor;
}
public void afterPropertiesSet() throws Exception {
if (this.securityInterceptor == null)
throw new IllegalArgumentException("securityInterceptor required");
}
}
}
----
====
In the preceding example, the security interceptor is applied to every instance of `PersistableEntity`, which is an abstract class not shown (you can use any other class or `pointcut` expression you like).
For those curious, `AspectJCallback` is needed because the `proceed();` statement has special meaning only within an `around()` body.
The `AspectJSecurityInterceptor` calls this anonymous `AspectJCallback` class when it wants the target object to continue.
You need to configure Spring to load the aspect and wire it with the `AspectJSecurityInterceptor`.
The following example shows a bean declaration that achieves this:
====
[source,xml]
----
<bean id="domainObjectInstanceSecurityAspect"
class="security.samples.aspectj.DomainObjectInstanceSecurityAspect"
factory-method="aspectOf">
<property name="securityInterceptor" ref="bankManagerSecurity"/>
</bean>
----
====
Now you can create your beans from anywhere within your application, using whatever means you think fit (e.g. `new Person();`), and they have the security interceptor applied.

View File

@ -27,7 +27,7 @@ The figure above builds off our xref:servlet/architecture.adoc#servlet-securityf
image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the `/private` resource for which the user is not authorized. image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the `/private` resource for which the user is not authorized.
image:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] indicates that the unauthenticated request is _Denied_ by throwing an `AccessDeniedException`. image:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-http-requests.adoc[`AuthorizationFilter`] indicates that the unauthenticated request is _Denied_ by throwing an `AccessDeniedException`.
image:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates _Start Authentication_. image:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates _Start Authentication_.
The configured xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of {security-api-url}org/springframework/security/oauth2/server/resource/authentication/BearerTokenAuthenticationEntryPoint.html[`BearerTokenAuthenticationEntryPoint`], which sends a `WWW-Authenticate` header. The configured xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of {security-api-url}org/springframework/security/oauth2/server/resource/authentication/BearerTokenAuthenticationEntryPoint.html[`BearerTokenAuthenticationEntryPoint`], which sends a `WWW-Authenticate` header.

View File

@ -16,7 +16,7 @@ The figure above builds off our xref:servlet/architecture.adoc#servlet-securityf
image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the `/private` resource, for which it is not authorized. image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the `/private` resource, for which it is not authorized.
image:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] indicates that the unauthenticated request is _Denied_ by throwing an `AccessDeniedException`. image:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-http-requests.adoc[`AuthorizationFilter`] indicates that the unauthenticated request is _Denied_ by throwing an `AccessDeniedException`.
image:{icondir}/number_3.png[] Since the user lacks authorization, the xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates _Start Authentication_. image:{icondir}/number_3.png[] Since the user lacks authorization, the xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates _Start Authentication_.
The configured xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of {security-api-url}org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.html[`LoginUrlAuthenticationEntryPoint`], which redirects to <<servlet-saml2login-sp-initiated-factory,the `<saml2:AuthnRequest>` generating endpoint>>, `Saml2WebSsoAuthenticationRequestFilter`. The configured xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of {security-api-url}org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.html[`LoginUrlAuthenticationEntryPoint`], which redirects to <<servlet-saml2login-sp-initiated-factory,the `<saml2:AuthnRequest>` generating endpoint>>, `Saml2WebSsoAuthenticationRequestFilter`.