Skip to content

Commit 22132c8

Browse files
Custom role prefix for anonymous authority
Closes gh-19673 Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
1 parent c9b653b commit 22132c8

4 files changed

Lines changed: 106 additions & 10 deletions

File tree

config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1024,7 +1024,7 @@ public HttpSecurity logout(Customizer<LogoutConfigurer<HttpSecurity>> logoutCust
10241024
* @return the {@link HttpSecurity} for further customizations @
10251025
*/
10261026
public HttpSecurity anonymous(Customizer<AnonymousConfigurer<HttpSecurity>> anonymousCustomizer) {
1027-
anonymousCustomizer.customize(getOrApply(new AnonymousConfigurer<>()));
1027+
anonymousCustomizer.customize(getOrApply(new AnonymousConfigurer<>(getContext())));
10281028
return HttpSecurity.this;
10291029
}
10301030

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919
import java.util.List;
2020
import java.util.UUID;
2121

22+
import org.springframework.context.ApplicationContext;
2223
import org.springframework.security.authentication.AnonymousAuthenticationProvider;
2324
import org.springframework.security.authentication.AuthenticationProvider;
2425
import org.springframework.security.config.Customizer;
2526
import org.springframework.security.config.annotation.SecurityConfigurer;
2627
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
2728
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
29+
import org.springframework.security.config.core.GrantedAuthorityDefaults;
2830
import org.springframework.security.core.Authentication;
2931
import org.springframework.security.core.GrantedAuthority;
3032
import org.springframework.security.core.authority.AuthorityUtils;
@@ -40,6 +42,7 @@
4042
*
4143
* @author Rob Winch
4244
* @author DingHao
45+
* @author Ngoc Nhan
4346
* @since 3.2
4447
*/
4548
public final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>>
@@ -53,15 +56,20 @@ public final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>>
5356

5457
private Object principal = "anonymousUser";
5558

56-
private List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS");
59+
private List<GrantedAuthority> authorities;
5760

5861
private String computedKey;
5962

6063
/**
61-
* Creates a new instance
64+
* Creates a new instance.
65+
* @param context the {@link ApplicationContext} to use
6266
* @see HttpSecurity#anonymous(Customizer)
6367
*/
64-
public AnonymousConfigurer() {
68+
public AnonymousConfigurer(ApplicationContext context) {
69+
GrantedAuthorityDefaults grantedAuthorityDefaults = context.getBeanProvider(GrantedAuthorityDefaults.class)
70+
.getIfAvailable();
71+
String rolePrefix = (grantedAuthorityDefaults != null) ? grantedAuthorityDefaults.getRolePrefix() : "ROLE_";
72+
this.authorities = AuthorityUtils.createAuthorityList(rolePrefix + "ANONYMOUS");
6573
}
6674

6775
/**

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

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,15 @@
2323
import org.springframework.beans.factory.annotation.Autowired;
2424
import org.springframework.context.annotation.Bean;
2525
import org.springframework.context.annotation.Configuration;
26+
import org.springframework.security.access.prepost.PreAuthorize;
2627
import org.springframework.security.authentication.AnonymousAuthenticationToken;
2728
import org.springframework.security.authentication.AuthenticationDetailsSource;
2829
import org.springframework.security.config.ObjectPostProcessor;
2930
import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
31+
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
3032
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
3133
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
34+
import org.springframework.security.config.core.GrantedAuthorityDefaults;
3235
import org.springframework.security.config.test.SpringTestContext;
3336
import org.springframework.security.config.test.SpringTestContextExtension;
3437
import org.springframework.security.core.annotation.AuthenticationPrincipal;
@@ -48,13 +51,15 @@
4851
import static org.mockito.Mockito.verify;
4952
import static org.springframework.security.config.Customizer.withDefaults;
5053
import static org.springframework.security.config.annotation.SecurityContextChangedListenerArgumentMatchers.setAuthentication;
54+
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.anonymous;
5155
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
5256
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
5357
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
5458

5559
/**
5660
* @author Rob Winch
5761
* @author Josh Cummings
62+
* @author Ngoc Nhan
5863
*/
5964
@ExtendWith(SpringTestContextExtension.class)
6065
public class AnonymousConfigurerTests {
@@ -116,6 +121,15 @@ public void anonymousAuthenticationWhenUsingAuthenticationDetailsSourceRefThenMa
116121
verify(source).buildDetails(any(HttpServletRequest.class));
117122
}
118123

124+
@Test
125+
public void anonymousWithCustomAuthorityDefaults() throws Exception {
126+
this.spring.register(AnonymousGrantedAuthorityDefaultsConfig.class, PrincipalController.class).autowire();
127+
this.mockMvc.perform(get("/anonymous").with(anonymous())).andExpect(status().isForbidden());
128+
this.mockMvc.perform(get("/anonymous").with(anonymous().authorities("my_roles_ANONYMOUS")))
129+
.andExpect(status().isOk())
130+
.andExpect(content().string("anonymous"));
131+
}
132+
119133
@Configuration
120134
@EnableWebSecurity
121135
@EnableWebMvc
@@ -261,6 +275,31 @@ String principal(@AuthenticationPrincipal String principal) {
261275
return principal;
262276
}
263277

278+
@GetMapping("/anonymous")
279+
@PreAuthorize("hasRole('ANONYMOUS')")
280+
String getAnonymous(@AuthenticationPrincipal String principal) {
281+
return principal;
282+
}
283+
284+
}
285+
286+
@EnableWebMvc
287+
@EnableWebSecurity
288+
@EnableMethodSecurity
289+
@Configuration
290+
static class AnonymousGrantedAuthorityDefaultsConfig {
291+
292+
@Bean
293+
GrantedAuthorityDefaults grantedAuthorityDefaults() {
294+
return new GrantedAuthorityDefaults("my_roles_");
295+
}
296+
297+
@Bean
298+
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
299+
http.authorizeHttpRequests((authorize) -> authorize.anyRequest().anonymous());
300+
return http.build();
301+
}
302+
264303
}
265304

266305
}

test/src/main/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessors.java

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
* Security.
118118
*
119119
* @author Rob Winch
120+
* @author Ngoc Nhan
120121
* @since 4.0
121122
*/
122123
public final class SecurityMockMvcRequestPostProcessors {
@@ -346,7 +347,7 @@ public static RequestPostProcessor authentication(Authentication authentication)
346347
* </code> </pre>
347348
* @return the {@link RequestPostProcessor} to use
348349
*/
349-
public static RequestPostProcessor anonymous() {
350+
public static AnonymousRequestPostProcessor anonymous() {
350351
return new AnonymousRequestPostProcessor();
351352
}
352353

@@ -1020,16 +1021,64 @@ private User createUser() {
10201021

10211022
}
10221023

1023-
private static class AnonymousRequestPostProcessor extends SecurityContextRequestPostProcessorSupport
1024+
public static final class AnonymousRequestPostProcessor extends SecurityContextRequestPostProcessorSupport
10241025
implements RequestPostProcessor {
10251026

1026-
private AuthenticationRequestPostProcessor delegate = new AuthenticationRequestPostProcessor(
1027-
new AnonymousAuthenticationToken("key", "anonymous",
1028-
AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")));
1027+
private String key = "key";
1028+
1029+
private Object principal = "anonymous";
1030+
1031+
private Collection<? extends GrantedAuthority> authorities = AuthorityUtils
1032+
.createAuthorityList("ROLE_ANONYMOUS");
10291033

10301034
@Override
10311035
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
1032-
return this.delegate.postProcessRequest(request);
1036+
AuthenticationRequestPostProcessor delegate = new AuthenticationRequestPostProcessor(
1037+
this.createAnonymous());
1038+
return delegate.postProcessRequest(request);
1039+
}
1040+
1041+
/**
1042+
* Creates a new {@link AnonymousAuthenticationToken}
1043+
* @return the {@link AnonymousAuthenticationToken} for the principal
1044+
*/
1045+
private AnonymousAuthenticationToken createAnonymous() {
1046+
return new AnonymousAuthenticationToken(this.key, this.principal, this.authorities);
1047+
}
1048+
1049+
/**
1050+
* Populates the user's {@link GrantedAuthority}'s. The default is
1051+
* {@code ROLE_ANONYMOUS}.
1052+
* @param authorities the authorities to grant to the anonymous user
1053+
* @return the {@link AnonymousRequestPostProcessor} for further customizations
1054+
*/
1055+
public AnonymousRequestPostProcessor authorities(String... authorities) {
1056+
Assert.notNull(authorities, "authorities cannot be null");
1057+
this.authorities = AuthorityUtils.createAuthorityList(authorities);
1058+
return this;
1059+
}
1060+
1061+
/**
1062+
* Sets the key used to identify the anonymous user.
1063+
* @param key the key to use
1064+
* @return the {@link AnonymousRequestPostProcessor} for further customizations
1065+
*/
1066+
public AnonymousRequestPostProcessor key(String key) {
1067+
Assert.hasLength(key, "key cannot be null or empty");
1068+
this.key = key;
1069+
return this;
1070+
}
1071+
1072+
/**
1073+
* Sets the principal for the anonymous user.
1074+
* @param principal the principal to use
1075+
* @return the {@link AnonymousRequestPostProcessor} for further customizations
1076+
*/
1077+
public AnonymousRequestPostProcessor principal(Object principal) {
1078+
Assert.notNull(principal, "principal cannot be null");
1079+
Assert.isTrue(!"".equals(principal), "principal cannot be empty");
1080+
this.principal = principal;
1081+
return this;
10331082
}
10341083

10351084
}

0 commit comments

Comments
 (0)