Skip to content

Commit f2b2e60

Browse files
quaffjzheaux
authored andcommitted
Replace static "ROLE_" with customized role prefix
Fix gh-4134
1 parent df6ed74 commit f2b2e60

File tree

2 files changed

+152
-15
lines changed

2 files changed

+152
-15
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -76,6 +76,7 @@
7676
*
7777
* @param <H> the type of {@link HttpSecurityBuilder} that is being configured
7878
* @author Rob Winch
79+
* @author Yanming Zhou
7980
* @since 3.2
8081
* @see org.springframework.security.config.annotation.web.builders.HttpSecurity#authorizeRequests()
8182
*/
@@ -94,6 +95,8 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
9495

9596
private static final String rememberMe = "rememberMe";
9697

98+
private final String rolePrefix;
99+
97100
private final ExpressionInterceptUrlRegistry REGISTRY;
98101

99102
private SecurityExpressionHandler<FilterInvocation> expressionHandler;
@@ -103,6 +106,15 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
103106
* @see HttpSecurity#authorizeRequests()
104107
*/
105108
public ExpressionUrlAuthorizationConfigurer(ApplicationContext context) {
109+
String[] grantedAuthorityDefaultsBeanNames = context.getBeanNamesForType(GrantedAuthorityDefaults.class);
110+
if (grantedAuthorityDefaultsBeanNames.length == 1) {
111+
GrantedAuthorityDefaults grantedAuthorityDefaults = context.getBean(grantedAuthorityDefaultsBeanNames[0],
112+
GrantedAuthorityDefaults.class);
113+
this.rolePrefix = grantedAuthorityDefaults.getRolePrefix();
114+
}
115+
else {
116+
this.rolePrefix = "ROLE_";
117+
}
106118
this.REGISTRY = new ExpressionInterceptUrlRegistry(context);
107119
}
108120

@@ -176,16 +188,16 @@ private SecurityExpressionHandler<FilterInvocation> getExpressionHandler(H http)
176188
return this.expressionHandler;
177189
}
178190

179-
private static String hasAnyRole(String... authorities) {
180-
String anyAuthorities = StringUtils.arrayToDelimitedString(authorities, "','ROLE_");
181-
return "hasAnyRole('ROLE_" + anyAuthorities + "')";
191+
private static String hasAnyRole(String rolePrefix, String... authorities) {
192+
String anyAuthorities = StringUtils.arrayToDelimitedString(authorities, "','" + rolePrefix);
193+
return "hasAnyRole('" + rolePrefix + anyAuthorities + "')";
182194
}
183195

184-
private static String hasRole(String role) {
196+
private static String hasRole(String rolePrefix, String role) {
185197
Assert.notNull(role, "role cannot be null");
186-
Assert.isTrue(!role.startsWith("ROLE_"),
187-
() -> "role should not start with 'ROLE_' since it is automatically inserted. Got '" + role + "'");
188-
return "hasRole('ROLE_" + role + "')";
198+
Assert.isTrue(rolePrefix.isEmpty() || !role.startsWith(rolePrefix), () -> "role should not start with '"
199+
+ rolePrefix + "' since it is automatically inserted. Got '" + role + "'");
200+
return "hasRole('" + rolePrefix + role + "')";
189201
}
190202

191203
private static String hasAuthority(String authority) {
@@ -308,27 +320,30 @@ public AuthorizedUrl not() {
308320

309321
/**
310322
* Shortcut for specifying URLs require a particular role. If you do not want to
311-
* have "ROLE_" automatically inserted see {@link #hasAuthority(String)}.
323+
* have role prefix (default "ROLE_") automatically inserted see
324+
* {@link #hasAuthority(String)}.
312325
* @param role the role to require (i.e. USER, ADMIN, etc). Note, it should not
313-
* start with "ROLE_" as this is automatically inserted.
326+
* start with role prefix as this is automatically inserted.
314327
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
315328
* customization
316329
*/
317330
public ExpressionInterceptUrlRegistry hasRole(String role) {
318-
return access(ExpressionUrlAuthorizationConfigurer.hasRole(role));
331+
return access(ExpressionUrlAuthorizationConfigurer
332+
.hasRole(ExpressionUrlAuthorizationConfigurer.this.rolePrefix, role));
319333
}
320334

321335
/**
322336
* Shortcut for specifying URLs require any of a number of roles. If you do not
323-
* want to have "ROLE_" automatically inserted see
337+
* want to have role prefix (default "ROLE_") automatically inserted see
324338
* {@link #hasAnyAuthority(String...)}
325339
* @param roles the roles to require (i.e. USER, ADMIN, etc). Note, it should not
326-
* start with "ROLE_" as this is automatically inserted.
340+
* start with role prefix as this is automatically inserted.
327341
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
328342
* customization
329343
*/
330344
public ExpressionInterceptUrlRegistry hasAnyRole(String... roles) {
331-
return access(ExpressionUrlAuthorizationConfigurer.hasAnyRole(roles));
345+
return access(ExpressionUrlAuthorizationConfigurer
346+
.hasAnyRole(ExpressionUrlAuthorizationConfigurer.this.rolePrefix, roles));
332347
}
333348

334349
/**

config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurerTests.java

Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -41,6 +41,7 @@
4141
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
4242
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
4343
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
44+
import org.springframework.security.config.core.GrantedAuthorityDefaults;
4445
import org.springframework.security.config.test.SpringTestContext;
4546
import org.springframework.security.config.test.SpringTestContextExtension;
4647
import org.springframework.security.core.Authentication;
@@ -75,6 +76,7 @@
7576
*
7677
* @author Rob Winch
7778
* @author Eleftheria Stein
79+
* @author Yanming Zhou
7880
*/
7981
@ExtendWith(SpringTestContextExtension.class)
8082
public class ExpressionUrlAuthorizationConfigurerTests {
@@ -232,6 +234,28 @@ public void getWhenHasAnyRoleUserConfiguredAndRoleIsAdminThenRespondsWithForbidd
232234
this.mvc.perform(requestWithAdmin).andExpect(status().isForbidden());
233235
}
234236

237+
@Test
238+
public void getWhenHasAnyRoleUserWithTestRolePrefixConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception {
239+
this.spring.register(RoleUserWithTestRolePrefixConfig.class, BasicController.class).autowire();
240+
// @formatter:off
241+
MockHttpServletRequestBuilder requestWithUser = get("/")
242+
.with(user("user")
243+
.authorities(new SimpleGrantedAuthority("TEST_USER")));
244+
// @formatter:on
245+
this.mvc.perform(requestWithUser).andExpect(status().isOk());
246+
}
247+
248+
@Test
249+
public void getWhenHasAnyRoleUserWithEmptyRolePrefixConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception {
250+
this.spring.register(RoleUserWithEmptyRolePrefixConfig.class, BasicController.class).autowire();
251+
// @formatter:off
252+
MockHttpServletRequestBuilder requestWithUser = get("/")
253+
.with(user("user")
254+
.authorities(new SimpleGrantedAuthority("USER")));
255+
// @formatter:on
256+
this.mvc.perform(requestWithUser).andExpect(status().isOk());
257+
}
258+
235259
@Test
236260
public void getWhenRoleUserOrAdminConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception {
237261
this.spring.register(RoleUserOrAdminConfig.class, BasicController.class).autowire();
@@ -263,6 +287,28 @@ public void getWhenRoleUserOrAdminConfiguredAndRoleIsOtherThenRespondsWithForbid
263287
this.mvc.perform(requestWithRoleOther).andExpect(status().isForbidden());
264288
}
265289

290+
@Test
291+
public void getWhenRoleUserOrAdminWithTestRolePrefixConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception {
292+
this.spring.register(RoleUserOrAdminWithTestRolePrefixConfig.class, BasicController.class).autowire();
293+
// @formatter:off
294+
MockHttpServletRequestBuilder requestWithUser = get("/")
295+
.with(user("user")
296+
.authorities(new SimpleGrantedAuthority("TEST_USER")));
297+
// @formatter:on
298+
this.mvc.perform(requestWithUser).andExpect(status().isOk());
299+
}
300+
301+
@Test
302+
public void getWhenRoleUserOrAdminWithEmptyRolePrefixConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception {
303+
this.spring.register(RoleUserOrAdminWithEmptyRolePrefixConfig.class, BasicController.class).autowire();
304+
// @formatter:off
305+
MockHttpServletRequestBuilder requestWithUser = get("/")
306+
.with(user("user")
307+
.authorities(new SimpleGrantedAuthority("USER")));
308+
// @formatter:on
309+
this.mvc.perform(requestWithUser).andExpect(status().isOk());
310+
}
311+
266312
@Test
267313
public void getWhenHasIpAddressConfiguredAndIpAddressMatchesThenRespondsWithOk() throws Exception {
268314
this.spring.register(HasIpAddressConfig.class, BasicController.class).autowire();
@@ -628,6 +674,44 @@ protected void configure(HttpSecurity http) throws Exception {
628674

629675
}
630676

677+
@EnableWebSecurity
678+
static class RoleUserWithTestRolePrefixConfig extends WebSecurityConfigurerAdapter {
679+
680+
@Override
681+
protected void configure(HttpSecurity http) throws Exception {
682+
// @formatter:off
683+
http
684+
.authorizeRequests()
685+
.anyRequest().hasAnyRole("USER");
686+
// @formatter:on
687+
}
688+
689+
@Bean
690+
GrantedAuthorityDefaults grantedAuthorityDefaults() {
691+
return new GrantedAuthorityDefaults("TEST_");
692+
}
693+
694+
}
695+
696+
@EnableWebSecurity
697+
static class RoleUserWithEmptyRolePrefixConfig extends WebSecurityConfigurerAdapter {
698+
699+
@Override
700+
protected void configure(HttpSecurity http) throws Exception {
701+
// @formatter:off
702+
http
703+
.authorizeRequests()
704+
.anyRequest().hasAnyRole("USER");
705+
// @formatter:on
706+
}
707+
708+
@Bean
709+
GrantedAuthorityDefaults grantedAuthorityDefaults() {
710+
return new GrantedAuthorityDefaults("");
711+
}
712+
713+
}
714+
631715
@EnableWebSecurity
632716
static class RoleUserOrAdminConfig extends WebSecurityConfigurerAdapter {
633717

@@ -642,6 +726,44 @@ protected void configure(HttpSecurity http) throws Exception {
642726

643727
}
644728

729+
@EnableWebSecurity
730+
static class RoleUserOrAdminWithTestRolePrefixConfig extends WebSecurityConfigurerAdapter {
731+
732+
@Override
733+
protected void configure(HttpSecurity http) throws Exception {
734+
// @formatter:off
735+
http
736+
.authorizeRequests()
737+
.anyRequest().hasAnyRole("USER", "ADMIN");
738+
// @formatter:on
739+
}
740+
741+
@Bean
742+
GrantedAuthorityDefaults grantedAuthorityDefaults() {
743+
return new GrantedAuthorityDefaults("TEST_");
744+
}
745+
746+
}
747+
748+
@EnableWebSecurity
749+
static class RoleUserOrAdminWithEmptyRolePrefixConfig extends WebSecurityConfigurerAdapter {
750+
751+
@Override
752+
protected void configure(HttpSecurity http) throws Exception {
753+
// @formatter:off
754+
http
755+
.authorizeRequests()
756+
.anyRequest().hasAnyRole("USER", "ADMIN");
757+
// @formatter:on
758+
}
759+
760+
@Bean
761+
GrantedAuthorityDefaults grantedAuthorityDefaults() {
762+
return new GrantedAuthorityDefaults("");
763+
}
764+
765+
}
766+
645767
@EnableWebSecurity
646768
static class HasIpAddressConfig extends WebSecurityConfigurerAdapter {
647769

0 commit comments

Comments
 (0)