1

The Problem

In Spring Security 6 and Spring Boot 3, how can I remove the ROLE_ prefix using Java Configuration?

I have a project using Spring Security 6, Spring Boot 3 and Thymeleaf. When I display a user's roles in Thymeleaf page, using code below

<span sec:authentication="principal.authorities"></span>

It returns this:

[ROLE_SUPERVISOR]

What I Need

I need to remove the ROLE_ prefix. I'd like to change this in Spring Security configuration using Java Configuration.

When I display a user's roles in a Thymeleaf page

<span sec:authentication="principal.authorities"></span>

I want it to display

[SUPERVISOR]

I want to remove the ROLE_ prefix. I want to change this in Spring Security using Java configuration.

My Environment

  • Spring Security 6.0.3
  • Spring Boot 3.0.6
  • Thymeleaf 3.1.1
  • Java 17.0.7

Research Performed

Based on the Spring Security 6 documentation, it said I could create a new GrantedAuthorityDefaults bean to remove the role prefix such as :

@Bean
static GrantedAuthorityDefaults grantedAuthorityDefaults() {
    return new GrantedAuthorityDefaults("");
}

I tried this and it doesn't work. It still shows the ROLE_ prefix for the roles in the Thymeleaf page even after restarting the Spring Boot app.

I also search stackoveflow. I found the stackoverflow posts listed below. But they didn't work. They are for previous versions of Spring Security 5. I need a solution for Spring Security 6 and Spring Boot 3.

How To Reproduce

I have a small Spring MVC application that using Spring Security 6 and Spring Boot 3.

1. My Spring Security configuration

File - 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. My Controller code

File - 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. My Thymeleaf page

File - 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. My Spring Boot application

File - 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. My Maven config file

File - 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. Run the code

When you run the Spring Boot app, you can view http://localhost:8080. Use the login Scott/tiger. The page displays the user role.

knelson59406
  • 751
  • 2
  • 6
  • 9
  • Why? You shouldn't show the technical role name, you should map it for the user with a human readable name. – dur May 12 '23 at 08:22
  • @dur - This is the task assignment I was given in the Jira ticket. Our tech lead wants to use the role names as mentioned above. Is this code an anti-pattern or bad practice? If so, can you post code or a link to a best practice solution? thanks you in advance. – knelson59406 May 12 '23 at 18:12
  • Spring's `UserBuilder` [adds "ROLE_" prefix automatically to passed argument](https://github.com/spring-projects/spring-security/blob/main/core/src/main/java/org/springframework/security/core/userdetails/User.java#L411) when calling `roles()` method, so you can't exclude it there. But you can use builder's `authorities()` method instead of `roles()` – Andrei Titov May 23 '23 at 10:58

1 Answers1

2

Least intrusive solution:

UserDetails scott = User.builder().username("scott").password("{noop}tiger")
                .authorities("SUPERVISOR"/*, ...*/).build();

To use authorities(...) instead of roles(...). (from my understatement this (the prefix) is the only difference between these "terms" ... but an "eternal" source of confusion and complexity)

The @Bean static GrantedAuthorityDefaults seems rather responsible for "evaluation" (hasRole/hasAuthority ?). (Unlike user details service: it "manages"/assigns...)

Enlighting: Difference between Role and GrantedAuthority in Spring Security

xerx593
  • 12,237
  • 5
  • 33
  • 64
  • Thanks for your speed response. Is there an alternative solution where I can continue to use `roles(...)`? – knelson59406 May 12 '23 at 18:10
  • No, not with (org.springframework.security...)"user builder" ! :) ..this is rather for "quick start"/dev ..in "production" you would have something "higher level"/more sophisticated :) – xerx593 May 12 '23 at 19:44