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
相关产品推荐
相关产品推荐

