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

Spring Boot如何访问自定义实体字段?是否需自定义UserDetails?

问题描述

我的User实体包含isPasswordChanged字段,希望在控制器和服务中访问该字段。

User实体代码

@Entity
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class User extends AbstractTimeEntity
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "user_id")
    private Long id;

    @Column(unique = true)
    private String username;

    @Column(unique = true)
    private String email;

    private String password;

    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.MERGE)
    @JoinTable(
            name = "user_role_junction",
            joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "user_id")},
            inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "role_id")}
    )
    private Set<Role> authorities = new HashSet<>();

    @Column(name = "is_password_changed")
    private boolean isPasswordChanged;

    public User(String username, String email, String password, Set<Role> authorities, boolean isPasswordChanged)
    {
        this.username = username;
        this.email = email;
        this.password = password;
        this.authorities = authorities;
        this.isPasswordChanged = isPasswordChanged;
    }
}

在我的UserDetailsService实现类中,尝试将isPasswordChanged字段添加到authenticatedUser中,但由于该字段并非UserDetails的字段,无法生效。我是否必须创建自己的UserDetails实现类,才能访问该字段以及ID等其他字段?

UserDetailsService实现类代码

@Service
public class AppUserDetailsService implements UserDetailsService
{
    private UserRepository userRepository;

    public AppUserDetailsService(UserRepository userRepository)
    {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
    {
        var user = userRepository.findByUsername(username);
        if(user != null)
        {
            User authenticatedUser =
                    new User(
                            user.getUsername(),
                            user.getPassword(),
                            user.getAuthorities().stream().map(role -> new SimpleGrantedAuthority(role.getAuthority())).collect(Collectors.toSet())
                    );
            return authenticatedUser;

        }else
        {
            throw new UsernameNotFoundException("Invalid username or password!");
        }
    }
}

解决方案

是的,你需要自定义UserDetails实现类来扩展Spring Security提供的UserDetails接口,这样才能在认证后直接获取到isPasswordChanged、ID等自定义字段。

步骤1:创建自定义UserDetails实现类

继承Spring Security的User类(或直接实现UserDetails接口),添加你的自定义字段及Getter方法:

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import java.util.Collection;

public class CustomUserDetails extends User {
    private final Long userId;
    private final boolean isPasswordChanged;

    public CustomUserDetails(String username, String password, Collection<? extends GrantedAuthority> authorities, Long userId, boolean isPasswordChanged) {
        super(username, password, authorities);
        this.userId = userId;
        this.isPasswordChanged = isPasswordChanged;
    }

    public Long getUserId() {
        return userId;
    }

    public boolean isPasswordChanged() {
        return isPasswordChanged;
    }
}

步骤2:修改UserDetailsService实现

在loadUserByUsername方法中返回自定义的CustomUserDetails实例,传入所需的实体字段:

@Service
public class AppUserDetailsService implements UserDetailsService
{
    private final UserRepository userRepository;

    public AppUserDetailsService(UserRepository userRepository)
    {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
    {
        var user = userRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("Invalid username or password!"));

        var authorities = user.getAuthorities().stream()
                .map(role -> new SimpleGrantedAuthority(role.getAuthority()))
                .collect(Collectors.toSet());

        return new CustomUserDetails(
                user.getUsername(),
                user.getPassword(),
                authorities,
                user.getId(),
                user.isPasswordChanged()
        );
    }
}

步骤3:在控制器/服务中访问自定义字段

通过SecurityContextHolder获取当前认证用户,强转为CustomUserDetails后即可直接调用Getter方法获取字段:

import org.springframework.security.core.context.SecurityContextHolder;

// 在控制器或服务方法中使用
CustomUserDetails currentUser = (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Long userId = currentUser.getUserId();
boolean passwordChanged = currentUser.isPasswordChanged();

替代方案(不推荐)

如果不想自定义UserDetails,也可以在需要字段时通过用户名重新从数据库查询User实体,但这种方式会多一次数据库查询,性能不如自定义UserDetails高效。


内容的提问来源于stack exchange,提问作者Jez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 05:25:18