
本文旨在指导如何在 spring security 6 中通过自定义 `userdetailsservice` 实现与外部数据库的用户认证。我们将介绍如何配置 `securityfilterchain`,并结合数据访问对象(dao)从外部数据库加载用户凭据,从而替代已弃用的 `websecurityconfigureradapter` 方法,提供一套现代且安全的用户登录解决方案。
在 Spring Security 6 中,传统的 WebSecurityConfigurerAdapter 类已被弃用,取而代之的是基于组件的配置方式,主要通过定义 SecurityFilterChain Bean 来实现安全配置。对于需要从外部数据库加载用户凭据进行认证的场景,核心思路是实现 UserDetailsService 接口,并通过数据访问层(DAO 或 Repository)与数据库进行交互。
核心概念:UserDetailsService 与数据访问层
Spring Security 的认证流程依赖于 UserDetailsService 接口。该接口只有一个方法:UserDetails loadUserByUsername(String username),它负责根据用户名加载用户的详细信息,包括用户名、密码以及所拥有的权限(角色)。
为了实现从外部数据库加载用户,我们需要:
一个数据访问对象 (DAO/Repository):负责与数据库进行实际的数据交互,查询用户数据。这可以是使用 JPA Repository、MyBatis Mapper,或者像 JdbcTemplate 这样更底层的 JDBC 访问方式。一个 UserDetailsService 的实现类:在该类中注入上述数据访问对象,并在 loadUserByUsername 方法中调用数据访问对象来获取用户数据,然后将其转换为 Spring Security 期望的 UserDetails 对象。
步骤一:配置 Spring Security 6 的 SecurityFilterChain
首先,我们需要一个 SecurityConfiguration 类来定义 SecurityFilterChain Bean,这是 Spring Security 6 的主要配置入口。在这个配置中,我们将定义授权规则和启用表单登录。Spring Security 会自动检测并使用容器中存在的 UserDetailsService 类型的 Bean 进行用户认证。
package de.gabriel.vertretungsplan.security;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.security.crypto.password.PasswordEncoder;import org.springframework.security.web.SecurityFilterChain;@Configuration@EnableWebSecuritypublic class SecurityConfiguration { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests((requests) -> requests // 定义不同角色的访问权限 .requestMatchers("/vertretungsplan").hasAnyRole("SCHUELER", "LEHRER", "VERWALTUNG") .requestMatchers("/account").hasAnyRole("LEHRER", "VERWALTUNG") .requestMatchers("/administration").hasRole("VERWALTUNG") // 允许所有用户访问根路径 .requestMatchers("/").permitAll() // 任何其他请求都需要认证 .anyRequest().authenticated() ) .formLogin(form -> form // 可以指定自定义的登录页面路径,例如 "/login" // .loginPage("/login") // 允许所有用户访问登录页面 .permitAll() ); return http.build(); } /** * 配置密码编码器。强烈建议在生产环境中使用强密码编码器。 */ @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }}
在上述配置中,我们定义了授权规则,并启用了 formLogin。重要的是,我们还定义了一个 PasswordEncoder Bean。Spring Security 在进行密码比对时会使用这个编码器。
步骤二:实现自定义 UserDetailsService
接下来,创建一个实现 UserDetailsService 接口的类。这个类将负责从数据库中检索用户信息。
Reclaim.ai
为优先事项创建完美的时间表
90 查看详情
首先,定义一个简单的用户实体类 UserEntity 来映射数据库中的用户表结构。
// src/main/java/your/package/model/UserEntity.javapackage your.package.model;import java.util.List;public class UserEntity { private String username; private String password; // 存储加密后的密码 private List roles; // 例如 "SCHUELER", "LEHRER", "VERWALTUNG" public UserEntity(String username, String password, List roles) { this.username = username; this.password = password; this.roles = roles; } // Getters public String getUsername() { return username; } public String getPassword() { return password; } public List getRoles() { return roles; } // Setters (根据需要添加)}
然后,实现 CustomUserDetailsService:
// src/main/java/your/package/security/CustomUserDetailsService.javapackage your.package.security;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.core.userdetails.UsernameNotFoundException;import org.springframework.stereotype.Service;import org.springframework.security.core.authority.SimpleGrantedAuthority;import your.package.model.UserEntity;import your.package.repository.UserRepository; // 引入你的用户数据访问接口import java.util.List;import java.util.stream.Collectors;@Service // 声明为一个Spring服务组件,使其可被Spring容器管理public class CustomUserDetailsService implements UserDetailsService { private final UserRepository userRepository; // 注入用户数据访问接口 public CustomUserDetailsService(UserRepository userRepository) { this.userRepository = userRepository; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 1. 从数据库获取用户实体 UserEntity userEntity = userRepository.findByUsername(username) .orElseThrow(() -> new UsernameNotFoundException("用户未找到: " + username)); // 2. 将用户实体中的角色转换为 Spring Security 期望的 SimpleGrantedAuthority 列表 // 注意:Spring Security 默认期望角色以 "ROLE_" 开头,例如 "ROLE_SCHUELER" List authorities = userEntity.getRoles().stream() .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) .collect(Collectors.toList()); // 3. 构建并返回 Spring Security 的 UserDetails 对象 return new org.springframework.security.core.userdetails.User( userEntity.getUsername(), // 用户名 userEntity.getPassword(), // 数据库中存储的加密密码 authorities // 用户权限列表 ); }}
步骤三:创建数据访问层(DAO/Repository)
为了让 CustomUserDetailsService 能够访问数据库,我们需要一个数据访问层。这里以 JdbcTemplate 为例,因为它轻量且易于理解。
首先,定义 UserRepository 接口:
// src/main/java/your/package/repository/UserRepository.javapackage your.package.repository;import your.package.model.UserEntity;import java.util.Optional;public interface UserRepository { Optional findByUsername(String username);}
然后,实现 JdbcUserRepository 使用 JdbcTemplate:
// src/main/java/your/package/repository/JdbcUserRepository.javapackage your.package.repository;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.jdbc.core.RowMapper;import org.springframework.stereotype.Repository;import your.package.model.UserEntity;import java.sql.ResultSet;import java.sql.SQLException;import java.util.Arrays;import java.util.List;import java.util.Optional;@Repository // 声明为一个Spring数据访问组件public class JdbcUserRepository implements UserRepository { private final JdbcTemplate jdbcTemplate; public JdbcUserRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public Optional findByUsername(String username) { // 假设数据库中有一个名为 'users' 的表,包含 'username', 'password', 'roles' 列 // 'roles' 列存储逗号分隔的角色字符串,例如 "SCHUELER,LEHRER" String sql = "SELECT username, password, roles FROM users WHERE username = ?"; try { return Optional.ofNullable(jdbcTemplate.queryForObject(sql, new UserEntityRowMapper(), username)); } catch (org.springframework.dao.EmptyResultDataAccessException e) { // 如果没有找到用户,JdbcTemplate 会抛出 EmptyResultDataAccessException return Optional.empty(); } } // 辅助类,用于将 ResultSet 的一行映射到 UserEntity 对象 private static class UserEntityRowMapper implements RowMapper { @Override public UserEntity mapRow(ResultSet rs, int rowNum) throws SQLException { String rolesString = rs.getString("roles"); List roles = Arrays.asList(rolesString.split(",")); // 将逗号分隔的字符串转换为列表 return new UserEntity( rs.getString("username"), rs.getString("password"), roles );
以上就是在 Spring Security 6 中集成外部数据库进行用户认证的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1063058.html
微信扫一扫
支付宝扫一扫