You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 22:35:03