Vaadin23+Spring Security实现数据库登录认证求助
实现基于数据库的UserDetailsService替代InMemoryUserDetailsManager
核心思路
Spring Security的UserDetailsService接口中,loadUserByUsername(String username)方法的参数就是登录表单提交的用户名——你无需手动获取,Spring Security会自动传递这个参数。你要做的就是注入已开发好的数据库操作类(仓库/服务),用这个参数查询用户实体,再转换为Spring Security所需的UserDetails对象即可。
步骤1:自定义UserDetailsService实现类
假设你已经有User实体类和对应的UserRepository(提供findByUsername查询方法),直接实现UserDetailsService接口:
@Service public class CustomUserDetailsService implements UserDetailsService { private final UserRepository userRepository; // 构造注入你已有的仓库类 public CustomUserDetailsService(UserRepository userRepository) { this.userRepository = userRepository; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 用登录用户名查询数据库,不存在则抛出异常 User dbUser = userRepository.findByUsername(username) .orElseThrow(() -> new UsernameNotFoundException("用户不存在: " + username)); // 将自定义User实体转换为Spring Security的UserDetails实例 return User.builder() .username(dbUser.getUsername()) .password(dbUser.getPassword()) // 注意:数据库中必须存加密后的密码 .roles(dbUser.getRoles().toArray(new String[0])) // 假设roles是集合类型,转成数组 .build(); } }
简化方案:让User实体直接实现UserDetails
如果不想每次都转换实体,可让你的User实体直接实现UserDetails接口,这样查询后可直接返回:
@Entity public class User implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; private String password; @ElementCollection(fetch = FetchType.EAGER) private Set<String> roles; // 实现UserDetails的所有方法 @Override public Collection<? extends GrantedAuthority> getAuthorities() { // 将角色转换为Spring Security的权限对象 return roles.stream() .map(SimpleGrantedAuthority::new) .collect(Collectors.toList()); } @Override public String getPassword() { return password; } @Override public String getUsername() { return username; } // 以下方法根据你的业务需求调整,默认返回true即可 @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } // 其他getter、setter方法 }
此时CustomUserDetailsService可简化为:
@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return userRepository.findByUsername(username) .orElseThrow(() -> new UsernameNotFoundException("用户不存在: " + username)); }
步骤2:在Spring Security配置中替换为自定义实现
修改你的Security配置类,注入自定义的CustomUserDetailsService,并替换掉InMemoryUserDetailsManager:
@Configuration @EnableWebSecurity public class SecurityConfig extends VaadinWebSecurityConfigurerAdapter { private final CustomUserDetailsService userDetailsService; public SecurityConfig(CustomUserDetailsService userDetailsService) { this.userDetailsService = userDetailsService; } @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); // 你的其他Vaadin/Spring Security配置(比如允许访问的路径等) } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService) .passwordEncoder(passwordEncoder()); // 必须配置密码编码器,对应数据库密码的加密规则 } @Bean public PasswordEncoder passwordEncoder() { // 示例用BCrypt加密,根据你实际的加密方式选择 return new BCryptPasswordEncoder(); } }
关键注意事项
- 密码必须加密存储:数据库中不能存明文密码,创建用户时必须用
PasswordEncoder加密后再存入。 - 仓库方法确保存在:
UserRepository中必须有findByUsername(String username)方法,Spring Data JPA可通过方法名自动生成实现,无需手写SQL。
内容的提问来源于stack exchange,提问作者Anil Patel
相关产品推荐
相关产品推荐

