Spring Security 6与Spring Boot 3中移除ROLE_前缀的Java配置方法
在Spring Security 6和Spring Boot 3环境下,如何通过Java配置移除角色的ROLE_前缀?
我有一个使用Spring Security 6、Spring Boot 3和Thymeleaf的项目,在Thymeleaf页面中使用以下代码展示用户角色:
<span sec:authentication="principal.authorities"></span>
页面返回结果为:
[ROLE_SUPERVISOR]
我需要移除ROLE_前缀,希望通过Spring Security的Java配置实现该修改。当在Thymeleaf页面展示用户角色时,希望显示为:
[SUPERVISOR]
- Spring Security 6.0.3
- Spring Boot 3.0.6
- Thymeleaf 3.1.1
- Java 17.0.7
根据Spring Security 6官方文档,创建GrantedAuthorityDefaults Bean来移除角色前缀:
@Bean static GrantedAuthorityDefaults grantedAuthorityDefaults() { return new GrantedAuthorityDefaults(""); }
但该方案未生效,重启Spring Boot应用后,Thymeleaf页面仍显示带ROLE_前缀的角色。同时查阅了Spring Security 5版本的解决方案,也不适用于当前版本。
以下是一个可复现问题的小型Spring MVC应用:
1. Spring Security配置(SecurityRules.java)
package com.testspring; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.provisioning.UserDetailsManager; @Configuration public class SecurityRules { @Bean public UserDetailsManager users() { UserDetails scott = User.builder().username("scott").password("{noop}tiger") .roles("SUPERVISOR").build(); return new InMemoryUserDetailsManager(scott); } }
2. Controller代码(TestController.java)
package com.testspring; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class TestController { @RequestMapping("/") public String indexView() { return "index"; } }
3. Thymeleaf页面(index.html)
<!DOCTYPE html> <html lang="en" xmlns:sec="http://www.thymeleaf.org/extras/spring-security"> <body> <span sec:authentication="principal.authorities"></span> </body></html>
4. Spring Boot应用类(TestSpringApplication.java)
package com.testspring; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class TestSpringApplication { public static void main(String[] args) { SpringApplication.run(TestSpringApplication.class, args); } }
5. Maven配置文件(pom.xml)
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.0.6</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.testspring</groupId> <artifactId>testspring</artifactId> <version>0.0.1-SNAPSHOT</version> <name>testspring</name> <description>Demo project for Spring Boot</description> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity6</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
6.运行步骤
启动Spring Boot应用,访问http://localhost:8080,使用账号scott/tiger登录,页面将显示带前缀的用户角色。
在Spring Security 6中,GrantedAuthorityDefaults仅作用于方法级别的安全校验,不会修改UserDetails中存储的GrantedAuthority对象的实际值。要让Thymeleaf页面显示不带ROLE_前缀的角色,可通过以下两种方式处理:
方案一:直接使用权限声明替代角色方法
修改Spring Security配置类,添加SecurityFilterChain配置移除默认角色前缀,同时在创建用户时使用authorities()方法(roles()会自动添加ROLE_前缀):
package com.testspring; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.provisioning.UserDetailsManager; import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity public class SecurityRules { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .anyRequest().authenticated() ) .formLogin(form -> form.permitAll()); // 移除默认的ROLE_角色前缀 http.authorizeHttpRequests().rolePrefix(""); return http.build(); } @Bean public UserDetailsManager users() { // 使用authorities直接声明权限,不自动添加ROLE_前缀 UserDetails scott = User.builder() .username("scott") .password("{noop}tiger") .authorities("SUPERVISOR") .build(); return new InMemoryUserDetailsManager(scott); } }
方案二:自定义权限映射器(保留roles()方法)
如果需要继续使用roles()方法,可以自定义GrantedAuthorityMapper来移除前缀,将其集成到认证流程中:
package com.testspring; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.provisioning.UserDetailsManager; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.authentication.AuthenticationManagerBuilder; import java.util.List; import java.util.stream.Collectors; @Configuration @EnableWebSecurity public class SecurityRules { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .anyRequest().authenticated() ) .formLogin(form -> form.permitAll()) .userDetailsService(users()) .authenticationManager(authenticationManager(http)); return http.build(); } @Bean public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception { AuthenticationManagerBuilder auth = http.getSharedObject(AuthenticationManagerBuilder.class); auth.userDetailsService(users()) .authoritiesMapper(grantedAuthoritiesMapper()); return auth.build(); } private GrantedAuthorityMapper grantedAuthoritiesMapper() { return authorities -> authorities.stream() .map(authority -> { if (authority.getAuthority().startsWith("ROLE_")) { return new SimpleGrantedAuthority(authority.getAuthority().substring(5)); } return authority; }) .collect(Collectors.toList()); } @Bean public UserDetailsManager users() { // 仍使用roles()方法,自动添加ROLE_前缀,后续由映射器移除 UserDetails scott = User.builder() .username("scott") .password("{noop}tiger") .roles("SUPERVISOR") .build(); return new InMemoryUserDetailsManager(scott); } }
验证效果
重启应用后,登录scott/tiger,Thymeleaf页面将显示[SUPERVISOR],符合需求。
内容的提问来源于stack exchange,提问作者knelson59406

