Course – LSS – NPI (cat=Spring Security)
announcement - icon

If you're working on a Spring Security (and especially an OAuth) implementation, definitely have a look at the Learn Spring Security course:

>> LEARN SPRING SECURITY

1. Overview

In this quick tutorial, we’ll look at how we can display the logged-in user’s information in Thymeleaf.

We’ll extend the project we built in our Spring Security with Thymeleaf article. First, we’ll add a custom model to store user information and the service to retrieve them. After that, we’ll display it using the Spring Security Dialect from the Thymeleaf Extras module.

2. UserDetails Implementation

UserDetails is an interface from Spring Security used to hold non-security-related user information.

We’ll create our implementation of the UserDetails interface with some custom fields as the model for storing our authenticated user details. But, to deal with fewer fields and methods, we’ll extend the default framework implementation, the User class:

public class CustomUserDetails extends User {

    private final String firstName;
    private final String lastName;
    private final String email;

    private CustomUserDetails(Builder builder) {
        super(builder.username, builder.password, builder.authorities);
        this.firstName = builder.firstName;
        this.lastName = builder.lastName;
        this.email = builder.email;
    }

    // omitting getters and static Builder class
}

3. UserDetailsService Implementation

The framework’s UserDetailsService single method interface is responsible for fetching the UserDetails during the authentication process.

Consequently, to be able to load our CustomUserDetails, we’ll need to implement the UserDetailsService interface. For our example, we’re going to hardcode and store the user details in a Map having the usernames as keys:

@Service
public class CustomUserDetailsService implements UserDetailsService {

    private final PasswordEncoder passwordEncoder;
    private final Map<String, CustomUserDetails> userRegistry = new HashMap<>();

    // omitting constructor

    @PostConstruct
    public void init() {
        userRegistry.put("user", new CustomUserDetails.Builder().withFirstName("Mark")
          .withLastName("Johnson")
          .withEmail("[email protected]")
          .withUsername("user")
          .withPassword(passwordEncoder.encode("password"))
          .withAuthorities(Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")))
          .build());
        userRegistry.put("admin", new CustomUserDetails.Builder().withFirstName("James")
          .withLastName("Davis")
          .withEmail("[email protected]")
          .withUsername("admin")
          .withPassword(passwordEncoder.encode("password"))
          .withAuthorities(Collections.singletonList(new SimpleGrantedAuthority("ROLE_ADMIN")))
          .build());
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        CustomUserDetails userDetails = userRegistry.get(username);
        if (userDetails == null) {
            throw new UsernameNotFoundException(username);
        }
        return userDetails;
    }
}

In addition, for implementing the required loadUserByUsername() method, we’re fetching the corresponding CustomUserDetails object from the registry Map by username. However, the user details would be stored and retrieved from a repository in a production environment.

4. Spring Security Configuration

Firstly, we need to add the UserDetailsService in Spring Security’s configuration, which will be wired to the CustomUserDetailsService implementation. Further, we’ll set it on the HttpSecurity instance through the corresponding method. The rest is just minimal security configuration requiring the user to be authenticated and configuring /login, /logout, and /index endpoints:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    private final UserDetailsService userDetailsService;

    // omitting constructor

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.userDetailsService(userDetailsService)
            .authorizeHttpRequests(authorizationManagerRequestMatcherRegistry -> authorizationManagerRequestMatcherRegistry
                    .anyRequest().authenticated())
            .formLogin(httpSecurityFormLoginConfigurer -> httpSecurityFormLoginConfigurer
                    .loginPage("/login").permitAll()
                    .defaultSuccessUrl("/index"))
            .logout(httpSecurityLogoutConfigurer -> httpSecurityLogoutConfigurer.permitAll()
                    .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                    .logoutSuccessUrl("/login"));
        return http.build();
    }
}

5. Display Logged-in User Information

The Thymeleaf Extras module gives access to the Authentication object, and with the Security Dialect, we can display logged-in user information on Thymelef pages.

The CustomUserDetails object is accessible through the principal field on the Authentication object. For instance, we can access the firstName field using sec:authentication=”principal.firstName”:

<!DOCTYPE html>
<html xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<title>Welcome to Spring Security Thymeleaf tutorial</title>
</head>
<body>
    <h2>Welcome</h2>
    <p>Spring Security Thymeleaf tutorial</p>
    <div sec:authorize="hasRole('USER')">Text visible to user.</div>
    <div sec:authorize="hasRole('ADMIN')">Text visible to admin.</div>
    <div sec:authorize="isAuthenticated()">Text visible only to authenticated users.</div>
    Authenticated username:
    <div sec:authentication="name"></div>
    Authenticated user's firstName:
    <div sec:authentication="principal.firstName"></div>
    Authenticated user's lastName:
    <div sec:authentication="principal.lastName"></div>
    Authenticated user's email:
    <div sec:authentication="principal.lastName"></div>
    Authenticated user roles:
    <div sec:authentication="principal.authorities"></div>
</body>
</html>

Alternatively, an equivalent syntax for writing the Security Dialect expressions without the sec:authentication attribute is using the Spring Expression Language. Therefore, we could display the firstName field using Spring Expression Language format if we are more comfortable with it:

<div th:text="${#authentication.principal.firstName}"></div>

6. Conclusion

In this article, we’ve seen how we can display the logged-in user’s information in Thymeleaf using Spring Security’s support in a Spring Boot application.

As always, the source code for the examples is available over on GitHub.

Course – LSS (cat=Security/Spring Security)

I just announced the new Learn Spring Security course, including the full material focused on the new OAuth2 stack in Spring Security:

>> CHECK OUT THE COURSE
res – Security (video) (cat=Security/Spring Security)
Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.