AuthorizationManagerFactories.when
Closes gh-18920
This commit is contained in:
@@ -108,23 +108,20 @@ include-code::./ValidDurationConfiguration[tag=httpSecurity,indent=0]
|
||||
|
||||
In our previous examples, MFA is a static decision per request.
|
||||
There are times when we might want to require MFA for some users, but not others.
|
||||
Determining if MFA is enabled per user can be achieved by creating a custom `AuthorizationManager` that conditionally requires factors based upon the `Authentication`.
|
||||
Determining if MFA is enabled per user can be achieved by using `AuthorizationManagerFactories.multiFactor().when` to conditionally require factors based upon the `Authentication`.
|
||||
This is implemented using xref:servlet/authorization/architecture.adoc#authz-conditional-authorization-manager[`ConditionalAuthorizationManager`].
|
||||
|
||||
include-code::./AdminMfaAuthorizationManagerConfiguration[tag=authorizationManager,indent=0]
|
||||
<1> MFA is required for the user with the username `admin`
|
||||
<2> Otherwise, MFA is not required
|
||||
|
||||
To enable the MFA rules globally, we can publish an `AuthorizationManagerFactory` Bean.
|
||||
To enable the conditional MFA rules globally, we can publish an `AuthorizationManagerFactory` Bean.
|
||||
|
||||
include-code::./AdminMfaAuthorizationManagerConfiguration[tag=authorizationManagerFactory,indent=0]
|
||||
<1> Inject the custom `AuthorizationManager` as the javadoc:org.springframework.security.authorization.DefaultAuthorizationManagerFactory#setAdditionalAuthorization(org.springframework.security.authorization.AuthorizationManager)[DefaultAuthorization.additionalAuthorization].
|
||||
This instructs `DefaultAuthorizationManagerFactory` that any authorization rule should apply our custom `AuthorizationManager` along with any authorization requirements defined by the application (e.g. `hasRole("ADMIN")`).
|
||||
<2> Publish `DefaultAuthorizationManagerFactory` as a Bean, so it is used globally
|
||||
<1> Require `FACTOR_OTT` and `FACTOR_PASSWORD`
|
||||
<2> Only apply the requirement if the username is `admin`. Otherwise, MFA is not required.
|
||||
<3> Return the `AuthorizationManagerFactory` using `.build()`. Since it is published as a Bean, it is used globally.
|
||||
|
||||
This should feel very similar to our previous example in xref:./mfa.adoc#authorization-manager-factory[].
|
||||
The difference is that in the previous example, the `AuthorizationManagerFactories` is setting `DefaultAuthorization.additionalAuthorization` with a built in `AuthorizationManager` that always requires the same authorities.
|
||||
The difference is that in the previous example, the `AuthorizationManagerFactories` creates an `AuthorizationManager` that always requires the same authorities.
|
||||
|
||||
We can now define our authorization rules which are combined with `AdminMfaAuthorizationManager`.
|
||||
We can now define our authorization rules which are combined with `AuthorizationManagerFactory`.
|
||||
|
||||
include-code::./AdminMfaAuthorizationManagerConfiguration[tag=httpSecurity,indent=0]
|
||||
<1> URLs that begin with `/admin/**` require `ROLE_ADMIN`.
|
||||
@@ -138,7 +135,7 @@ If we preferred, we could change our logic to enable MFA based upon the roles ra
|
||||
[[raam-mfa]]
|
||||
== RequiredAuthoritiesAuthorizationManager
|
||||
|
||||
We've demonstrated how we can dynamically determine the authorities for a particular user in xref:./mfa.adoc#programmatic-mfa[] using a custom `AuthorizationManager`.
|
||||
We've demonstrated how we can dynamically determine the authorities for a particular user in xref:./mfa.adoc#programmatic-mfa[] using `AuthorizationManagerFactories.multiFactor().when`.
|
||||
However, this is such a common scenario that Spring Security provides built in support using javadoc:org.springframework.security.authorization.RequiredAuthoritiesAuthorizationManager[] and javadoc:org.springframework.security.authorization.RequiredAuthoritiesRepository[].
|
||||
|
||||
Let's implement the same requirement that we did in xref:./mfa.adoc#programmatic-mfa[] using the built-in support.
|
||||
@@ -168,7 +165,7 @@ Our example uses an in memory mapping of usernames to the additional required au
|
||||
For more dynamic use cases that can be determined by the username, a custom implementation of javadoc:org.springframework.security.authorization.RequiredAuthoritiesRepository[] can be created.
|
||||
Possible examples would be looking up if a user has enabled MFA in an explicit setting, determining if a user has registered a passkey, etc.
|
||||
|
||||
For cases that need to determine MFA based upon the `Authentication`, a custom `AuthorizationManger` can be used as demonstrated in xref:./mfa.adoc#programmatic-mfa[].
|
||||
For cases that need to determine MFA based upon the `Authentication`, `AuthorizationManagerFactories.multiFactor().when` can be used as demonstrated in xref:./mfa.adoc#programmatic-mfa[].
|
||||
|
||||
|
||||
[[hasallauthorities]]
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* https://github.com/spring-projects/spring-security/pull/18634[gh-18634] - Added javadoc:org.springframework.security.web.util.matcher.InetAddressMatcher[]
|
||||
* https://github.com/spring-projects/spring-security/issues/18755[gh-18755] - Include `charset` in `WWW-Authenticate` header
|
||||
* Added xref:servlet/authorization/architecture.adoc#authz-conditional-authorization-manager[ConditionalAuthorizationManager]
|
||||
* Added `when` and `withWhen` conditions to `AuthorizationManagerFactories.multiFactor()` for xref:servlet/authentication/mfa.adoc#programmatic-mfa[Programmatic MFA]
|
||||
|
||||
== OAuth 2.0
|
||||
|
||||
|
||||
+9
-38
@@ -1,17 +1,9 @@
|
||||
package org.springframework.security.docs.servlet.authentication.programmaticmfa;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authorization.AllAuthoritiesAuthorizationManager;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.authorization.AuthorizationManagerFactories;
|
||||
import org.springframework.security.authorization.AuthorizationManagerFactory;
|
||||
import org.springframework.security.authorization.AuthorizationResult;
|
||||
import org.springframework.security.authorization.DefaultAuthorizationManagerFactory;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
@@ -23,7 +15,6 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;
|
||||
import org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@@ -46,36 +37,16 @@ class AdminMfaAuthorizationManagerConfiguration {
|
||||
}
|
||||
// end::httpSecurity[]
|
||||
|
||||
// tag::authorizationManager[]
|
||||
@Component
|
||||
class AdminMfaAuthorizationManager implements AuthorizationManager<Object> {
|
||||
@Override
|
||||
public AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication, Object context) {
|
||||
if ("admin".equals(authentication.get().getName())) {
|
||||
AuthorizationManager<Object> admins =
|
||||
AllAuthoritiesAuthorizationManager.hasAllAuthorities(
|
||||
FactorGrantedAuthority.OTT_AUTHORITY,
|
||||
FactorGrantedAuthority.PASSWORD_AUTHORITY
|
||||
);
|
||||
// <1>
|
||||
return admins.authorize(authentication, context);
|
||||
} else {
|
||||
// <2>
|
||||
return new AuthorizationDecision(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
// end::authorizationManager[]
|
||||
|
||||
// tag::authorizationManagerFactory[]
|
||||
@Bean
|
||||
AuthorizationManagerFactory<Object> authorizationManagerFactory(
|
||||
AdminMfaAuthorizationManager admins) {
|
||||
DefaultAuthorizationManagerFactory<Object> defaults = new DefaultAuthorizationManagerFactory<>();
|
||||
// <1>
|
||||
defaults.setAdditionalAuthorization(admins);
|
||||
// <2>
|
||||
return defaults;
|
||||
AuthorizationManagerFactory<Object> authorizationManagerFactory() {
|
||||
// <3>
|
||||
return AuthorizationManagerFactories.multiFactor()
|
||||
// <1>
|
||||
.requireFactors(FactorGrantedAuthority.OTT_AUTHORITY, FactorGrantedAuthority.PASSWORD_AUTHORITY)
|
||||
// <2>
|
||||
.when((auth) -> "admin".equals(auth.getName()))
|
||||
.build();
|
||||
}
|
||||
// end::authorizationManagerFactory[]
|
||||
|
||||
|
||||
+8
-28
@@ -14,8 +14,6 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager
|
||||
import org.springframework.security.web.SecurityFilterChain
|
||||
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler
|
||||
import org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler
|
||||
import org.springframework.stereotype.Component
|
||||
import java.util.function.Supplier
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@@ -40,34 +38,16 @@ internal class AdminMfaAuthorizationManagerConfiguration {
|
||||
}
|
||||
// end::httpSecurity[]
|
||||
|
||||
// tag::authorizationManager[]
|
||||
@Component
|
||||
internal open class AdminMfaAuthorizationManager : AuthorizationManager<Any> {
|
||||
override fun authorize(
|
||||
authentication: Supplier<out Authentication?>, context: Any): AuthorizationResult {
|
||||
return if ("admin" == authentication.get().name) {
|
||||
var admins =
|
||||
AllAuthoritiesAuthorizationManager.hasAllAuthorities<Any>(
|
||||
FactorGrantedAuthority.OTT_AUTHORITY,
|
||||
FactorGrantedAuthority.PASSWORD_AUTHORITY)
|
||||
// <1>
|
||||
admins.authorize(authentication, context)
|
||||
} else {
|
||||
// <2>
|
||||
AuthorizationDecision(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
// end::authorizationManager[]
|
||||
|
||||
// tag::authorizationManagerFactory[]
|
||||
@Bean
|
||||
fun authorizationManagerFactory(admins: AdminMfaAuthorizationManager): AuthorizationManagerFactory<Any> {
|
||||
val defaults = DefaultAuthorizationManagerFactory<Any>()
|
||||
// <1>
|
||||
defaults.setAdditionalAuthorization(admins)
|
||||
// <2>
|
||||
return defaults
|
||||
fun authorizationManagerFactory(): AuthorizationManagerFactory<Any> {
|
||||
// <3>
|
||||
return AuthorizationManagerFactories.multiFactor<Any>()
|
||||
// <1>
|
||||
.requireFactors(FactorGrantedAuthority.OTT_AUTHORITY, FactorGrantedAuthority.PASSWORD_AUTHORITY)
|
||||
// <2>
|
||||
.`when` { auth -> "admin" == auth.name }
|
||||
.build()
|
||||
}
|
||||
// end::authorizationManagerFactory[]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user