diff --git a/docs/modules/ROOT/pages/migration.adoc b/docs/modules/ROOT/pages/migration.adoc index eddd803527..9ce5a44c44 100644 --- a/docs/modules/ROOT/pages/migration.adoc +++ b/docs/modules/ROOT/pages/migration.adoc @@ -367,6 +367,98 @@ companion object { ---- ==== +==== Replace any custom method-security ``AccessDecisionManager``s + +Your application may have a custom {security-api-url}org/springframework/security/access/AccessDecisionManager.html[`AccessDecisionManager`] or {security-api-url}org/springframework/security/access/AccessDecisionVoter.html[`AccessDecisionVoter`] arrangement. +The preparation strategy will depend on your reason for each arrangement. +Read on to find the best match for your situation. + +===== I use `UnanimousBased` + +If your application uses {security-api-url}org/springframework/security/access/vote/UnanimousBased.html[`UnanimousBased`] with the default voters, you likely need do nothing since unanimous-based is the default behavior with {security-api-url}org/springframework/security/config/annotation/method/configuration/EnableMethodSecurity.html[`@EnableMethodSecurity`]. + +However, if you do discover that you cannot accept the default authorization managers, you can use `AuthorizationManagers.allOf` to compose your own arrangement. +Having done that, please follow the details in the reference manual for xref:servlet/authorization/method-security.adoc#jc-method-security-custom-authorization-manager[adding a custom `AuthorizationManager`]. + +===== I use `AffirmativeBased` + +If your application uses {security-api-url}org/springframework/security/access/vote/AffirmativeBased.html[`AffirmativeBased`], then you can construct an equivalent {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`], like so: + +==== +.Java +[source,java,role="primary"] +---- +AuthorizationManager authorization = AuthorizationManagers.anyOf( + // ... your list of authorization managers +) +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +val authorization = AuthorizationManagers.anyOf( + // ... your list of authorization managers +) +---- +==== + +Once you have implemented `AuthorizationManager`, please follow the details in the reference manual for xref:servlet/authorization/method-security.adoc#jc-method-security-custom-authorization-manager[adding a custom `AuthorizationManager`]. + +===== I use `ConsensusBased` + +There is no framework-provided equivalent for {security-api-url}org/springframework/security/access/vote/ConsensusBased.html[`ConsensusBased`]. +In that case, please implement a composite {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`] that takes the set of delegate ``AuthorizationManager``s into account. + +Once you have implemented `AuthorizationManager`, please follow the details in the reference manual for xref:servlet/authorization/method-security.adoc#jc-method-security-custom-authorization-manager[adding a custom `AuthorizationManager`]. + +===== I use a custom `AccessDecisionVoter` + +You should either change the class to implement {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`] or create an adapter. + +Without knowing what your custom voter is doing, it is impossible to recommend a general-purpose solution. +By way of example, though, here is what adapting {security-api-url}org/springframework/security/access/SecurityMetadataSource.html[`SecurityMetadataSource`] and {security-api-url}org/springframework/security/access/AccessDecisionVoter.html[`AccessDecisionVoter`] for `@PreAuthorize` would look like: + +==== +.Java +[source,java,role="primary"] +---- +public final class PreAuthorizeAuthorizationManagerAdapter implements AuthorizationManager { + private final SecurityMetadataSource metadata; + private final AccessDecisionVoter voter; + + public PreAuthorizeAuthorizationManagerAdapter(MethodSecurityExpressionHandler expressionHandler) { + ExpressionBasedAnnotationAttributeFactory attributeFactory = + new ExpressionBasedAnnotationAttributeFactory(expressionHandler); + this.metadata = new PrePostAnnotationSecurityMetadataSource(attributeFactory); + ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice(); + expressionAdvice.setExpressionHandler(expressionHandler); + this.voter = new PreInvocationAuthorizationAdviceVoter(expressionAdvice); + } + + public AuthorizationDecision check(Supplier authentication, MethodInvocation invocation) { + List attributes = this.metadata.getAttributes(invocation, AopUtils.getTargetClass(invocation.getThis())); + int decision = this.voter.vote(authentication.get(), invocation, attributes); + if (decision == ACCESS_GRANTED) { + return new AuthorizationDecision(true); + } + if (decision == ACCESS_DENIED) { + return new AuthorizationDecision(false); + } + return null; // abstain + } +} +---- +==== + +Once you have implemented `AuthorizationManager`, please follow the details in the reference manual for xref:servlet/authorization/method-security.adoc#jc-method-security-custom-authorization-manager[adding a custom `AuthorizationManager`]. + +===== I use a custom `AfterInvocationManager` + +{security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`] replaces both {security-api-url}org/springframework/security/access/AccessDecisionManager.html[`AccessDecisionManager`] and {security-api-url}org/springframework/security/access/intercept/AfterInvocationManager.html[`AfterInvocationManager`]. +The difference is that `AuthorizationManager` replaces `AccessDecisionManager` and `AuthorizationManager` replaces `AfterInvocationManager`. + +Given that, <<_i_use_a_custom_accessdecisionvoter,the same rules apply for adaptation>>, where the goal this time is to implement `AuthorizationManager` instead of `AuthorizationManager` and use `AuthorizationManagerAfterMethodInterceptor` instead of `AuthorizationManagerBeforeMethodInterceptor`. + [[servlet-check-for-annotationconfigurationexceptions]] ==== Check for ``AnnotationConfigurationException``s @@ -1099,6 +1191,215 @@ http { ---- ==== +==== Replace any custom filter-security ``AccessDecisionManager``s + +Your application may have a custom {security-api-url}org/springframework/security/access/AccessDecisionManager.html[`AccessDecisionManager`] or {security-api-url}org/springframework/security/access/AccessDecisionVoter.html[`AccessDecisionVoter`] arrangement. +The preparation strategy will depend on your reason for each arrangement. +Read on to find the best match for your situation. + +===== I use `UnanimousBased` + +If your application uses {security-api-url}org/springframework/security/access/vote/UnanimousBased.html[`UnanimousBased`], you should first adapt or replace any ``AccessDecisionVoter``s and then you can construct an `AuthorizationManager` like so: + +==== +.Java +[source,java,role="primary"] +---- +@Bean +AuthorizationManager requestAuthorization() { + PolicyAuthorizationManager policy = ...; + LocalAuthorizationManager local = ...; + return AuthorizationMangers.allOf(policy, local); +} +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +@Bean +fun requestAuthorization(): AuthorizationManager { + val policy: PolicyAuthorizationManager = ... + val local: LocalAuthorizationManager = ... + return AuthorizationMangers.allOf(policy, local) +} +---- + +.Xml +[source,xml,role="secondary"] +---- + + + + + + + + +---- +==== + +then, wire it into the DSL like so: + +==== +.Java +[source,java,role="primary"] +---- +http + .authorizeHttpRequests((authorize) -> authorize.anyRequest().access(requestAuthorization)) + // ... +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +http { + authorizeHttpRequests { + authorize(anyRequest, requestAuthorization) + } + // ... +} +---- + +.Xml +[source,xml,role="secondary"] +---- + +---- +==== + +[NOTE] +==== +`authorizeHttpRequests` is designed so that you can apply a custom `AuthorizationManager` to any url pattern. +See xref:servlet/authorization/authorize-http-requests.adoc#custom-authorization-manager[the reference] for more details. +==== + +===== I use `AffirmativeBased` + +If your application uses {security-api-url}org/springframework/security/access/vote/AffirmativeBased.html[`AffirmativeBased`], then you can construct an equivalent {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`], like so: + +==== +.Java +[source,java,role="primary"] +---- +@Bean +AuthorizationManager requestAuthorization() { + PolicyAuthorizationManager policy = ...; + LocalAuthorizationManager local = ...; + return AuthorizationMangers.anyOf(policy, local); +} +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +@Bean +fun requestAuthorization(): AuthorizationManager { + val policy: PolicyAuthorizationManager = ... + val local: LocalAuthorizationManager = ... + return AuthorizationMangers.anyOf(policy, local) +} +---- + +.Xml +[source,xml,role="secondary"] +---- + + + + + + + + +---- +==== + +then, wire it into the DSL like so: + +==== +.Java +[source,java,role="primary"] +---- +http + .authorizeHttpRequests((authorize) -> authorize.anyRequest().access(requestAuthorization)) + // ... +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +http { + authorizeHttpRequests { + authorize(anyRequest, requestAuthorization) + } + // ... +} +---- + +.Xml +[source,xml,role="secondary"] +---- + +---- +==== + +[NOTE] +==== +`authorizeHttpRequests` is designed so that you can apply a custom `AuthorizationManager` to any url pattern. +See xref:servlet/authorization/authorize-http-requests.adoc#custom-authorization-manager[the reference] for more details. +==== + +===== I use `ConsensusBased` + +There is no framework-provided equivalent for {security-api-url}org/springframework/security/access/vote/ConsensusBased.html[`ConsensusBased`]. +In that case, please implement a composite {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`] that takes the set of delegate ``AuthorizationManager``s into account. + +Once you have implemented `AuthorizationManager`, please follow the details in the reference manual for xref:servlet/authorization/authorize-http-requests.adoc#custom-authorization-manager[adding a custom `AuthorizationManager`]. + +===== I use a custom `AccessDecisionVoter` + +You should either change the class to implement {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[`AuthorizationManager`] or create an adapter. + + +Without knowing what your custom voter is doing, it is impossible to recommend a general-purpose solution. +By way of example, though, here is what adapting {security-api-url}org/springframework/security/access/SecurityMetadataSource.html[`SecurityMetadataSource`] and {security-api-url}org/springframework/security/access/AccessDecisionVoter.html[`AccessDecisionVoter`] for `anyRequest().authenticated()` would look like: + +==== +.Java +[source,java,role="primary"] +---- +public final class AnyRequestAuthenticatedAuthorizationManagerAdapter implements AuthorizationManager { + private final SecurityMetadataSource metadata; + private final AccessDecisionVoter voter; + + public PreAuthorizeAuthorizationManagerAdapter(SecurityExpressionHandler expressionHandler) { + Map> requestMap = Collections.singletonMap( + AnyRequestMatcher.INSTANCE, Collections.singletonList(new SecurityConfig("authenticated"))); + this.metadata = new DefaultFilterInvocationSecurityMetadataSource(requestMap); + WebExpressionVoter voter = new WebExpressionVoter(); + voter.setExpressionHandler(expressionHandler); + this.voter = voter; + } + + public AuthorizationDecision check(Supplier authentication, RequestAuthorizationContext context) { + List attributes = this.metadata.getAttributes(context); + int decision = this.voter.vote(authentication.get(), invocation, attributes); + if (decision == ACCESS_GRANTED) { + return new AuthorizationDecision(true); + } + if (decision == ACCESS_DENIED) { + return new AuthorizationDecision(false); + } + return null; // abstain + } +} +---- +==== + +Once you have implemented `AuthorizationManager`, please follow the details in the reference manual for xref:servlet/authorization/authorize-http-requests.adoc#custom-authorization-manager[adding a custom `AuthorizationManager`]. + [[servlet-authorizationmanager-requests-opt-out]] ==== Opt-out Steps diff --git a/docs/modules/ROOT/pages/servlet/authorization/authorize-http-requests.adoc b/docs/modules/ROOT/pages/servlet/authorization/authorize-http-requests.adoc index a0c6a444f9..a7282ed875 100644 --- a/docs/modules/ROOT/pages/servlet/authorization/authorize-http-requests.adoc +++ b/docs/modules/ROOT/pages/servlet/authorization/authorize-http-requests.adoc @@ -131,6 +131,7 @@ AuthorizationManager requestMatcherAuthorizationMan You can also wire xref:servlet/authorization/architecture.adoc#authz-custom-authorization-manager[your own custom authorization managers] for any request matcher. +[[custom-authorization-manager]] Here is an example of mapping a custom authorization manager to the `my/authorized/endpoint`: .Custom Authorization Manager