1. 概述

在这个快速教程中,我们将探讨如何在Thymeleaf中显示已登录用户的详细信息。

我们将基于我们在Spring Security与Thymeleaf集成文章中构建的项目进行扩展。首先,我们将添加一个自定义模型来存储用户信息,并实现获取它们的服务。然后,我们将使用Thymeleaf Extensions模块的Spring Security方言来展示这些信息。

2. UserDetails 实现

UserDetails 是Spring Security提供的接口,用于存储与安全无关的用户信息。

我们将创建一个UserDetails接口的自定义实现,其中包含一些用于存储已认证用户详细信息的自定义字段。为了处理较少的字段和方法,我们将继承默认框架实现——User类:

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 实现

框架的UserDetailsService单方法接口负责在身份验证过程中获取UserDetails

因此,为了加载我们的CustomUserDetails,我们需要实现UserDetailsService接口。在示例中,我们将硬编码并存储用户详细信息在一个以用户名为键的Map中:

@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;
    }
}

此外,为了实现所需的loadUserByUsername()方法,我们将通过用户名从注册表Map中获取相应的CustomUserDetails对象。但在生产环境中,用户详细信息将存储和从仓库中检索。

4. Spring Security 配置

首先,我们需要在Spring Security配置中添加UserDetailsService,并与CustomUserDetailsService实现关联。然后,我们通过相应的方法将其设置到HttpSecurity实例上。其余的只是基本的安全配置,要求用户进行身份验证,并配置/login/logout/index端点:

@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. 显示已登录用户信息

Thymeleaf Extensions模块提供了对Authentication对象的访问,通过Security Dialect,我们可以在Thymeleaf页面上显示已登录用户的详细信息。

CustomUserDetails对象可以通过Authentication对象的principal字段访问。例如,我们可以使用sec:authentication=”principal.firstName”来访问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>

另一种等效的语法是在不使用sec:authentication属性的情况下编写Security Dialect表达式,即使用Spring Expression Language。如果我们更熟悉Spring Expression Language,我们可以使用其格式显示firstName字段:

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

6. 总结

在这篇文章中,我们了解了如何在Spring Boot应用中使用Spring Security的支持在Thymeleaf中显示已登录用户的详细信息。

如往常一样,示例代码可在GitHub上找到:https://github.com/eugenp/tutorials/tree/master/spring-security-modules/spring-security-web-thymeleaf