diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java index 949b833da43..1c0499693f8 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -76,6 +76,7 @@ * * @param the type of {@link HttpSecurityBuilder} that is being configured * @author Rob Winch + * @author Yanming Zhou * @since 3.2 * @see org.springframework.security.config.annotation.web.builders.HttpSecurity#authorizeRequests() */ @@ -94,6 +95,8 @@ public final class ExpressionUrlAuthorizationConfigurer expressionHandler; @@ -103,6 +106,15 @@ public final class ExpressionUrlAuthorizationConfigurer getExpressionHandler(H http) return this.expressionHandler; } - private static String hasAnyRole(String... authorities) { - String anyAuthorities = StringUtils.arrayToDelimitedString(authorities, "','ROLE_"); - return "hasAnyRole('ROLE_" + anyAuthorities + "')"; + private static String hasAnyRole(String rolePrefix, String... authorities) { + String anyAuthorities = StringUtils.arrayToDelimitedString(authorities, "','" + rolePrefix); + return "hasAnyRole('" + rolePrefix + anyAuthorities + "')"; } - private static String hasRole(String role) { + private static String hasRole(String rolePrefix, String role) { Assert.notNull(role, "role cannot be null"); - Assert.isTrue(!role.startsWith("ROLE_"), - () -> "role should not start with 'ROLE_' since it is automatically inserted. Got '" + role + "'"); - return "hasRole('ROLE_" + role + "')"; + Assert.isTrue(rolePrefix.isEmpty() || !role.startsWith(rolePrefix), () -> "role should not start with '" + + rolePrefix + "' since it is automatically inserted. Got '" + role + "'"); + return "hasRole('" + rolePrefix + role + "')"; } private static String hasAuthority(String authority) { @@ -308,27 +320,30 @@ public AuthorizedUrl not() { /** * Shortcut for specifying URLs require a particular role. If you do not want to - * have "ROLE_" automatically inserted see {@link #hasAuthority(String)}. + * have role prefix (default "ROLE_") automatically inserted see + * {@link #hasAuthority(String)}. * @param role the role to require (i.e. USER, ADMIN, etc). Note, it should not - * start with "ROLE_" as this is automatically inserted. + * start with role prefix as this is automatically inserted. * @return the {@link ExpressionUrlAuthorizationConfigurer} for further * customization */ public ExpressionInterceptUrlRegistry hasRole(String role) { - return access(ExpressionUrlAuthorizationConfigurer.hasRole(role)); + return access(ExpressionUrlAuthorizationConfigurer + .hasRole(ExpressionUrlAuthorizationConfigurer.this.rolePrefix, role)); } /** * Shortcut for specifying URLs require any of a number of roles. If you do not - * want to have "ROLE_" automatically inserted see + * want to have role prefix (default "ROLE_") automatically inserted see * {@link #hasAnyAuthority(String...)} * @param roles the roles to require (i.e. USER, ADMIN, etc). Note, it should not - * start with "ROLE_" as this is automatically inserted. + * start with role prefix as this is automatically inserted. * @return the {@link ExpressionUrlAuthorizationConfigurer} for further * customization */ public ExpressionInterceptUrlRegistry hasAnyRole(String... roles) { - return access(ExpressionUrlAuthorizationConfigurer.hasAnyRole(roles)); + return access(ExpressionUrlAuthorizationConfigurer + .hasAnyRole(ExpressionUrlAuthorizationConfigurer.this.rolePrefix, roles)); } /** diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurerTests.java index ff0c6a5d692..2eff3fe9df7 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,6 +41,7 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.core.GrantedAuthorityDefaults; import org.springframework.security.config.test.SpringTestContext; import org.springframework.security.config.test.SpringTestContextExtension; import org.springframework.security.core.Authentication; @@ -75,6 +76,7 @@ * * @author Rob Winch * @author Eleftheria Stein + * @author Yanming Zhou */ @ExtendWith(SpringTestContextExtension.class) public class ExpressionUrlAuthorizationConfigurerTests { @@ -232,6 +234,28 @@ public void getWhenHasAnyRoleUserConfiguredAndRoleIsAdminThenRespondsWithForbidd this.mvc.perform(requestWithAdmin).andExpect(status().isForbidden()); } + @Test + public void getWhenHasAnyRoleUserWithTestRolePrefixConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception { + this.spring.register(RoleUserWithTestRolePrefixConfig.class, BasicController.class).autowire(); + // @formatter:off + MockHttpServletRequestBuilder requestWithUser = get("/") + .with(user("user") + .authorities(new SimpleGrantedAuthority("TEST_USER"))); + // @formatter:on + this.mvc.perform(requestWithUser).andExpect(status().isOk()); + } + + @Test + public void getWhenHasAnyRoleUserWithEmptyRolePrefixConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception { + this.spring.register(RoleUserWithEmptyRolePrefixConfig.class, BasicController.class).autowire(); + // @formatter:off + MockHttpServletRequestBuilder requestWithUser = get("/") + .with(user("user") + .authorities(new SimpleGrantedAuthority("USER"))); + // @formatter:on + this.mvc.perform(requestWithUser).andExpect(status().isOk()); + } + @Test public void getWhenRoleUserOrAdminConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception { this.spring.register(RoleUserOrAdminConfig.class, BasicController.class).autowire(); @@ -263,6 +287,28 @@ public void getWhenRoleUserOrAdminConfiguredAndRoleIsOtherThenRespondsWithForbid this.mvc.perform(requestWithRoleOther).andExpect(status().isForbidden()); } + @Test + public void getWhenRoleUserOrAdminWithTestRolePrefixConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception { + this.spring.register(RoleUserOrAdminWithTestRolePrefixConfig.class, BasicController.class).autowire(); + // @formatter:off + MockHttpServletRequestBuilder requestWithUser = get("/") + .with(user("user") + .authorities(new SimpleGrantedAuthority("TEST_USER"))); + // @formatter:on + this.mvc.perform(requestWithUser).andExpect(status().isOk()); + } + + @Test + public void getWhenRoleUserOrAdminWithEmptyRolePrefixConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception { + this.spring.register(RoleUserOrAdminWithEmptyRolePrefixConfig.class, BasicController.class).autowire(); + // @formatter:off + MockHttpServletRequestBuilder requestWithUser = get("/") + .with(user("user") + .authorities(new SimpleGrantedAuthority("USER"))); + // @formatter:on + this.mvc.perform(requestWithUser).andExpect(status().isOk()); + } + @Test public void getWhenHasIpAddressConfiguredAndIpAddressMatchesThenRespondsWithOk() throws Exception { this.spring.register(HasIpAddressConfig.class, BasicController.class).autowire(); @@ -628,6 +674,44 @@ protected void configure(HttpSecurity http) throws Exception { } + @EnableWebSecurity + static class RoleUserWithTestRolePrefixConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .anyRequest().hasAnyRole("USER"); + // @formatter:on + } + + @Bean + GrantedAuthorityDefaults grantedAuthorityDefaults() { + return new GrantedAuthorityDefaults("TEST_"); + } + + } + + @EnableWebSecurity + static class RoleUserWithEmptyRolePrefixConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .anyRequest().hasAnyRole("USER"); + // @formatter:on + } + + @Bean + GrantedAuthorityDefaults grantedAuthorityDefaults() { + return new GrantedAuthorityDefaults(""); + } + + } + @EnableWebSecurity static class RoleUserOrAdminConfig extends WebSecurityConfigurerAdapter { @@ -642,6 +726,44 @@ protected void configure(HttpSecurity http) throws Exception { } + @EnableWebSecurity + static class RoleUserOrAdminWithTestRolePrefixConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .anyRequest().hasAnyRole("USER", "ADMIN"); + // @formatter:on + } + + @Bean + GrantedAuthorityDefaults grantedAuthorityDefaults() { + return new GrantedAuthorityDefaults("TEST_"); + } + + } + + @EnableWebSecurity + static class RoleUserOrAdminWithEmptyRolePrefixConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .anyRequest().hasAnyRole("USER", "ADMIN"); + // @formatter:on + } + + @Bean + GrantedAuthorityDefaults grantedAuthorityDefaults() { + return new GrantedAuthorityDefaults(""); + } + + } + @EnableWebSecurity static class HasIpAddressConfig extends WebSecurityConfigurerAdapter {