Replace static "ROLE_" with customized role prefix

Fix gh-4134
This commit is contained in:
Yanming Zhou 2021-07-15 16:40:10 +08:00 committed by Josh Cummings
parent df6ed74303
commit f2b2e6002f
2 changed files with 152 additions and 15 deletions

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -76,6 +76,7 @@ import org.springframework.util.StringUtils;
* *
* @param <H> the type of {@link HttpSecurityBuilder} that is being configured * @param <H> the type of {@link HttpSecurityBuilder} that is being configured
* @author Rob Winch * @author Rob Winch
* @author Yanming Zhou
* @since 3.2 * @since 3.2
* @see org.springframework.security.config.annotation.web.builders.HttpSecurity#authorizeRequests() * @see org.springframework.security.config.annotation.web.builders.HttpSecurity#authorizeRequests()
*/ */
@ -94,6 +95,8 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
private static final String rememberMe = "rememberMe"; private static final String rememberMe = "rememberMe";
private final String rolePrefix;
private final ExpressionInterceptUrlRegistry REGISTRY; private final ExpressionInterceptUrlRegistry REGISTRY;
private SecurityExpressionHandler<FilterInvocation> expressionHandler; private SecurityExpressionHandler<FilterInvocation> expressionHandler;
@ -103,6 +106,15 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
* @see HttpSecurity#authorizeRequests() * @see HttpSecurity#authorizeRequests()
*/ */
public ExpressionUrlAuthorizationConfigurer(ApplicationContext context) { public ExpressionUrlAuthorizationConfigurer(ApplicationContext context) {
String[] grantedAuthorityDefaultsBeanNames = context.getBeanNamesForType(GrantedAuthorityDefaults.class);
if (grantedAuthorityDefaultsBeanNames.length == 1) {
GrantedAuthorityDefaults grantedAuthorityDefaults = context.getBean(grantedAuthorityDefaultsBeanNames[0],
GrantedAuthorityDefaults.class);
this.rolePrefix = grantedAuthorityDefaults.getRolePrefix();
}
else {
this.rolePrefix = "ROLE_";
}
this.REGISTRY = new ExpressionInterceptUrlRegistry(context); this.REGISTRY = new ExpressionInterceptUrlRegistry(context);
} }
@ -176,16 +188,16 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
return this.expressionHandler; return this.expressionHandler;
} }
private static String hasAnyRole(String... authorities) { private static String hasAnyRole(String rolePrefix, String... authorities) {
String anyAuthorities = StringUtils.arrayToDelimitedString(authorities, "','ROLE_"); String anyAuthorities = StringUtils.arrayToDelimitedString(authorities, "','" + rolePrefix);
return "hasAnyRole('ROLE_" + anyAuthorities + "')"; 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.notNull(role, "role cannot be null");
Assert.isTrue(!role.startsWith("ROLE_"), Assert.isTrue(rolePrefix.isEmpty() || !role.startsWith(rolePrefix), () -> "role should not start with '"
() -> "role should not start with 'ROLE_' since it is automatically inserted. Got '" + role + "'"); + rolePrefix + "' since it is automatically inserted. Got '" + role + "'");
return "hasRole('ROLE_" + role + "')"; return "hasRole('" + rolePrefix + role + "')";
} }
private static String hasAuthority(String authority) { private static String hasAuthority(String authority) {
@ -308,27 +320,30 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
/** /**
* Shortcut for specifying URLs require a particular role. If you do not want to * 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 * @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 * @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization * customization
*/ */
public ExpressionInterceptUrlRegistry hasRole(String role) { 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 * 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...)} * {@link #hasAnyAuthority(String...)}
* @param roles the roles to require (i.e. USER, ADMIN, etc). Note, it should not * @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 * @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization * customization
*/ */
public ExpressionInterceptUrlRegistry hasAnyRole(String... roles) { public ExpressionInterceptUrlRegistry hasAnyRole(String... roles) {
return access(ExpressionUrlAuthorizationConfigurer.hasAnyRole(roles)); return access(ExpressionUrlAuthorizationConfigurer
.hasAnyRole(ExpressionUrlAuthorizationConfigurer.this.rolePrefix, roles));
} }
/** /**

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -41,6 +41,7 @@ import org.springframework.security.config.annotation.authentication.builders.Au
import org.springframework.security.config.annotation.web.builders.HttpSecurity; 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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 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.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension; import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
@ -75,6 +76,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* *
* @author Rob Winch * @author Rob Winch
* @author Eleftheria Stein * @author Eleftheria Stein
* @author Yanming Zhou
*/ */
@ExtendWith(SpringTestContextExtension.class) @ExtendWith(SpringTestContextExtension.class)
public class ExpressionUrlAuthorizationConfigurerTests { public class ExpressionUrlAuthorizationConfigurerTests {
@ -232,6 +234,28 @@ public class ExpressionUrlAuthorizationConfigurerTests {
this.mvc.perform(requestWithAdmin).andExpect(status().isForbidden()); 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 @Test
public void getWhenRoleUserOrAdminConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception { public void getWhenRoleUserOrAdminConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception {
this.spring.register(RoleUserOrAdminConfig.class, BasicController.class).autowire(); this.spring.register(RoleUserOrAdminConfig.class, BasicController.class).autowire();
@ -263,6 +287,28 @@ public class ExpressionUrlAuthorizationConfigurerTests {
this.mvc.perform(requestWithRoleOther).andExpect(status().isForbidden()); 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 @Test
public void getWhenHasIpAddressConfiguredAndIpAddressMatchesThenRespondsWithOk() throws Exception { public void getWhenHasIpAddressConfiguredAndIpAddressMatchesThenRespondsWithOk() throws Exception {
this.spring.register(HasIpAddressConfig.class, BasicController.class).autowire(); this.spring.register(HasIpAddressConfig.class, BasicController.class).autowire();
@ -628,6 +674,44 @@ public class ExpressionUrlAuthorizationConfigurerTests {
} }
@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 @EnableWebSecurity
static class RoleUserOrAdminConfig extends WebSecurityConfigurerAdapter { static class RoleUserOrAdminConfig extends WebSecurityConfigurerAdapter {
@ -642,6 +726,44 @@ public class ExpressionUrlAuthorizationConfigurerTests {
} }
@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 @EnableWebSecurity
static class HasIpAddressConfig extends WebSecurityConfigurerAdapter { static class HasIpAddressConfig extends WebSecurityConfigurerAdapter {