1
0
mirror of synced 2026-08-03 16:56:56 +00:00

Add AuthorizationManagerFactory.hasAll(Authorities|Roles)

Closes gh-17932
This commit is contained in:
Rob Winch
2025-09-18 09:08:46 -05:00
parent ebc391cb97
commit 675835e525
8 changed files with 88 additions and 4 deletions
@@ -296,6 +296,17 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
return access(this.authorizationManagerFactory.hasAnyRole(roles));
}
/**
* Specifies that a user requires all the provided roles.
* @param roles the roles that the user should have (i.e. ADMIN, USER, etc). Each
* role should not start with ROLE_ since it is automatically prepended already
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
* customizations
*/
public AuthorizationManagerRequestMatcherRegistry hasAllRoles(String... roles) {
return access(this.authorizationManagerFactory.hasAllRoles(roles));
}
/**
* Specifies a user requires an authority.
* @param authority the authority that should be required
@@ -317,6 +328,17 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
return access(this.authorizationManagerFactory.hasAnyAuthority(authorities));
}
/**
* Specifies that a user requires all the provided authorities.
* @param authorities the authorities that the user should have (i.e. ROLE_USER,
* ROLE_ADMIN, etc)
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
* customizations
*/
public AuthorizationManagerRequestMatcherRegistry hasAllAuthorities(String... authorities) {
return access(this.authorizationManagerFactory.hasAllAuthorities(authorities));
}
/**
* Specify that URLs are allowed by any authenticated user.
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
@@ -190,9 +190,11 @@ public class AuthorizeHttpRequestsConfigurerTests {
verify(authorizationManagerFactory).permitAll();
verify(authorizationManagerFactory).denyAll();
verify(authorizationManagerFactory).hasRole("ADMIN");
verify(authorizationManagerFactory).hasAllRoles("hasAllRoles1", "hasAllRoles2");
verify(authorizationManagerFactory).hasAnyRole("USER", "ADMIN");
verify(authorizationManagerFactory).hasAuthority("write");
verify(authorizationManagerFactory).hasAnyAuthority("resource.read", "read");
verify(authorizationManagerFactory).hasAllAuthorities("hasAllAuthorities1", "hasAllAuthorities2");
verify(authorizationManagerFactory).authenticated();
verify(authorizationManagerFactory).fullyAuthenticated();
verify(authorizationManagerFactory).rememberMe();
@@ -823,8 +825,10 @@ public class AuthorizeHttpRequestsConfigurerTests {
given(authorizationManagerFactory.denyAll()).willReturn(authorizationManager);
given(authorizationManagerFactory.hasRole(anyString())).willReturn(authorizationManager);
given(authorizationManagerFactory.hasAnyRole(any(String[].class))).willReturn(authorizationManager);
given(authorizationManagerFactory.hasAllRoles(any(String[].class))).willReturn(authorizationManager);
given(authorizationManagerFactory.hasAuthority(anyString())).willReturn(authorizationManager);
given(authorizationManagerFactory.hasAnyAuthority(any(String[].class))).willReturn(authorizationManager);
given(authorizationManagerFactory.hasAllAuthorities(any(String[].class))).willReturn(authorizationManager);
given(authorizationManagerFactory.authenticated()).willReturn(authorizationManager);
given(authorizationManagerFactory.fullyAuthenticated()).willReturn(authorizationManager);
given(authorizationManagerFactory.rememberMe()).willReturn(authorizationManager);
@@ -1116,8 +1120,10 @@ public class AuthorizeHttpRequestsConfigurerTests {
.requestMatchers("/private").denyAll()
.requestMatchers("/admin").hasRole("ADMIN")
.requestMatchers("/user").hasAnyRole("USER", "ADMIN")
.requestMatchers("/hasAllRoles").hasAllRoles("hasAllRoles1", "hasAllRoles2")
.requestMatchers(HttpMethod.POST, "/resource").hasAuthority("write")
.requestMatchers("/resource").hasAnyAuthority("resource.read", "read")
.requestMatchers("/hasAllAuthorities").hasAllAuthorities("hasAllAuthorities1", "hasAllAuthorities2")
.requestMatchers("/authenticated").authenticated()
.requestMatchers("/fully-authenticated").fullyAuthenticated()
.requestMatchers("/remember-me").rememberMe()
@@ -65,6 +65,17 @@ public interface AuthorizationManagerFactory<T extends @Nullable Object> {
return AuthorityAuthorizationManager.hasAnyRole(roles);
}
/**
* Creates an {@link AuthorizationManager} that requires users to have all the
* provided roles.
* @param roles the roles (automatically prepended with ROLE_) that the user must have
* to allow access (i.e. USER, ADMIN, etc.)
* @return A new {@link AuthorizationManager} instance
*/
default AuthorizationManager<T> hasAllRoles(String... roles) {
return AllAuthoritiesAuthorizationManager.hasAllRoles(roles);
}
/**
* Creates an {@link AuthorizationManager} that requires users to have the specified
* authority.
@@ -87,6 +98,17 @@ public interface AuthorizationManagerFactory<T extends @Nullable Object> {
return AuthorityAuthorizationManager.hasAnyAuthority(authorities);
}
/**
* Creates an {@link AuthorizationManager} that requires users to have all the
* provided authorities.
* @param authorities the authorities that the user must have to allow access (i.e.
* USER, ADMIN, etc.)
* @return A new {@link AuthorizationManager} instance
*/
default AuthorizationManager<T> hasAllAuthorities(String... authorities) {
return AllAuthoritiesAuthorizationManager.hasAllAuthorities(authorities);
}
/**
* Creates an {@link AuthorizationManager} that allows any authenticated user.
* @return A new {@link AuthorizationManager} instance
@@ -79,6 +79,11 @@ public final class DefaultAuthorizationManagerFactory<T extends @Nullable Object
return withRoleHierarchy(AuthorityAuthorizationManager.hasAnyRole(this.rolePrefix, roles));
}
@Override
public AuthorizationManager<T> hasAllRoles(String... roles) {
return withRoleHierarchy(AllAuthoritiesAuthorizationManager.hasAllPrefixedAuthorities(this.rolePrefix, roles));
}
@Override
public AuthorizationManager<T> hasAuthority(String authority) {
return withRoleHierarchy(AuthorityAuthorizationManager.hasAuthority(authority));
@@ -89,6 +94,11 @@ public final class DefaultAuthorizationManagerFactory<T extends @Nullable Object
return withRoleHierarchy(AuthorityAuthorizationManager.hasAnyAuthority(authorities));
}
@Override
public AuthorizationManager<T> hasAllAuthorities(String... authorities) {
return withRoleHierarchy(AllAuthoritiesAuthorizationManager.hasAllAuthorities(authorities));
}
@Override
public AuthorizationManager<T> authenticated() {
return withTrustResolver(AuthenticatedAuthorizationManager.authenticated());
@@ -114,6 +124,12 @@ public final class DefaultAuthorizationManagerFactory<T extends @Nullable Object
return authorizationManager;
}
private AllAuthoritiesAuthorizationManager<T> withRoleHierarchy(
AllAuthoritiesAuthorizationManager<T> authorizationManager) {
authorizationManager.setRoleHierarchy(this.roleHierarchy);
return authorizationManager;
}
private AuthenticatedAuthorizationManager<T> withTrustResolver(
AuthenticatedAuthorizationManager<T> authorizationManager) {
authorizationManager.setTrustResolver(this.trustResolver);
@@ -55,6 +55,13 @@ public class AuthorizationManagerFactoryTests {
assertThat(authorizationManager).isInstanceOf(AuthorityAuthorizationManager.class);
}
@Test
public void hasAllRolesReturnsAllAuthoritiesAuthorizationManagerByDefault() {
AuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();
AuthorizationManager<String> authorizationManager = factory.hasAllRoles("authority1", "authority2");
assertThat(authorizationManager).isInstanceOf(AllAuthoritiesAuthorizationManager.class);
}
@Test
public void hasAuthorityReturnsAuthorityAuthorizationManagerByDefault() {
AuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();
@@ -69,6 +76,13 @@ public class AuthorizationManagerFactoryTests {
assertThat(authorizationManager).isInstanceOf(AuthorityAuthorizationManager.class);
}
@Test
public void hasAllAuthoritiesReturnsAllAuthoritiesAuthorizationManagerByDefault() {
AuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();
AuthorizationManager<String> authorizationManager = factory.hasAllAuthorities("authority1", "authority2");
assertThat(authorizationManager).isInstanceOf(AllAuthoritiesAuthorizationManager.class);
}
@Test
public void authenticatedReturnsAuthenticatedAuthorizationManagerByDefault() {
AuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();
@@ -157,8 +157,10 @@ public interface AuthorizationManagerFactory<T> {
AuthorizationManager<T> denyAll();
AuthorizationManager<T> hasRole(String role);
AuthorizationManager<T> hasAnyRole(String... roles);
AuthorizationManager<T> hasAllRoles(String... roles);
AuthorizationManager<T> hasAuthority(String authority);
AuthorizationManager<T> hasAnyAuthority(String... authorities);
AuthorizationManager<T> hasAllAuthorities(String... authorities);
AuthorizationManager<T> authenticated();
AuthorizationManager<T> fullyAuthenticated();
AuthorizationManager<T> rememberMe();
@@ -720,7 +720,9 @@ As a quick summary, here are the authorization rules built into the DSL:
* `hasAuthority` - The request requires that the `Authentication` have xref:servlet/authorization/architecture.adoc#authz-authorities[a `GrantedAuthority`] that matches the given value
* `hasRole` - A shortcut for `hasAuthority` that prefixes `ROLE_` or whatever is configured as the default prefix
* `hasAnyAuthority` - The request requires that the `Authentication` have a `GrantedAuthority` that matches any of the given values
* `hasAnyRole` - A shortcut for `hasAnyAuthority` that prefixes `ROLE_` or whatever is configured as the default prefix
* `hasAnyRole` - A shortcut for `hasAnyAuthority` that prefixes `ROLE_` or whatever is configured as the default prefix* `hasAnyAuthority` - The request requires that the `Authentication` have a `GrantedAuthority` that matches any of the given values
* `hasAllRoles` - A shortcut for `hasAllAuthorities` that prefixes `ROLE_` or whatever is configured as the default prefix
* `hasAllAuthorities` - The request requires that the `Authentication` have a `GrantedAuthority` that matches all of the given values
* `access` - The request uses this custom `AuthorizationManager` to determine access
Having now learned the patterns, rules, and how they can be paired together, you should be able to understand what is going on in this more complex example:
@@ -746,7 +748,7 @@ SecurityFilterChain web(HttpSecurity http) throws Exception {
.dispatcherTypeMatchers(FORWARD, ERROR).permitAll() // <2>
.requestMatchers("/static/**", "/signup", "/about").permitAll() // <3>
.requestMatchers("/admin/**").hasRole("ADMIN") // <4>
.requestMatchers("/db/**").access(allOf(hasAuthority("db"), hasRole("ADMIN"))) // <5>
.requestMatchers("/db/**").hasAllAuthorities("db", "ROLE_ADMIN") // <5>
.anyRequest().denyAll() // <6>
);
@@ -762,7 +764,7 @@ Specifically, any user can access a request if the URL starts with "/static/", e
<4> 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.
<5> Any URL that starts with "/db/" requires the user to have both been granted the "db" permission as well as be a "ROLE_ADMIN".
You will notice that since we are using the `hasRole` expression we do not need to specify the "ROLE_" prefix.
You will notice that since we are using the `hasAllAuthorities` expression we must specify the "ROLE_" prefix.
<6> 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.
+1 -1
View File
@@ -16,7 +16,7 @@ Each section that follows will indicate the more notable removals as well as the
== Core
* Removed `AuthorizationManager#check` in favor of `AuthorizationManager#authorize`
* Added javadoc:org.springframework.security.authorization.AllAuthoritiesAuthorizationManager[] and javadoc:org.springframework.security.authorization.AllAuthoritiesReactiveAuthorizationManager[]
* Added javadoc:org.springframework.security.authorization.AllAuthoritiesAuthorizationManager[] and javadoc:org.springframework.security.authorization.AllAuthoritiesReactiveAuthorizationManager[] along with corresponding methods for xref:servlet/authorization/authorize-http-requests.adoc#authorize-requests[Authorizing `HttpServletRequests`]
* Added xref:servlet/authorization/architecture.adoc#authz-authorization-manager-factory[`AuthorizationManagerFactory`] for creating `AuthorizationManager` instances in xref:servlet/authorization/authorize-http-requests.adoc#customizing-authorization-managers[request-based] and xref:servlet/authorization/method-security.adoc#customizing-authorization-managers[method-based] authorization components
* Added `Authentication.Builder` for mutating and merging `Authentication` instances
* Moved Access API (`AccessDecisionManager`, `AccessDecisionVoter`, etc.) to a new module, `spring-security-access`