首页 > Java > java教程 > 正文

在 Spring Security 6 中集成外部数据库进行用户认证

霞舞
发布: 2025-11-29 11:49:35
原创
274人浏览过

在 Spring Security 6 中集成外部数据库进行用户认证

本文旨在指导如何在 spring security 6 中通过自定义 `userdetailsservice` 实现与外部数据库的用户认证。我们将介绍如何配置 `securityfilterchain`,并结合数据访问对象(dao)从外部数据库加载用户凭据,从而替代已弃用的 `websecurityconfigureradapter` 方法,提供一套现代且安全的用户登录解决方案。

在 Spring Security 6 中,传统的 WebSecurityConfigurerAdapter 类已被弃用,取而代之的是基于组件的配置方式,主要通过定义 SecurityFilterChain Bean 来实现安全配置。对于需要从外部数据库加载用户凭据进行认证的场景,核心思路是实现 UserDetailsService 接口,并通过数据访问层(DAO 或 Repository)与数据库进行交互。

核心概念:UserDetailsService 与数据访问层

Spring Security 的认证流程依赖于 UserDetailsService 接口。该接口只有一个方法:UserDetails loadUserByUsername(String username),它负责根据用户名加载用户的详细信息,包括用户名、密码以及所拥有的权限(角色)。

为了实现从外部数据库加载用户,我们需要:

  1. 一个数据访问对象 (DAO/Repository):负责与数据库进行实际的数据交互,查询用户数据。这可以是使用 JPA Repository、MyBatis Mapper,或者像 JdbcTemplate 这样更底层的 JDBC 访问方式。
  2. 一个 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
@EnableWebSecurity
public 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 接口的类。这个类将负责从数据库中检索用户信息。

Magic Write
Magic Write

Canva旗下AI文案生成器

Magic Write 75
查看详情 Magic Write

首先,定义一个简单的用户实体类 UserEntity 来映射数据库中的用户表结构。

// src/main/java/your/package/model/UserEntity.java
package your.package.model;

import java.util.List;

public class UserEntity {
    private String username;
    private String password; // 存储加密后的密码
    private List<String> roles; // 例如 "SCHUELER", "LEHRER", "VERWALTUNG"

    public UserEntity(String username, String password, List<String> roles) {
        this.username = username;
        this.password = password;
        this.roles = roles;
    }

    // Getters
    public String getUsername() { return username; }
    public String getPassword() { return password; }
    public List<String> getRoles() { return roles; }

    // Setters (根据需要添加)
}
登录后复制

然后,实现 CustomUserDetailsService:

// src/main/java/your/package/security/CustomUserDetailsService.java
package 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<SimpleGrantedAuthority> 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.java
package your.package.repository;

import your.package.model.UserEntity;
import java.util.Optional;

public interface UserRepository {
    Optional<UserEntity> findByUsername(String username);
}
登录后复制

然后,实现 JdbcUserRepository 使用 JdbcTemplate:

// src/main/java/your/package/repository/JdbcUserRepository.java
package 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<UserEntity> 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<UserEntity> {
        @Override
        public UserEntity mapRow(ResultSet rs, int rowNum) throws SQLException {
            String rolesString = rs.getString("roles");
            List<String> roles = Arrays.asList(rolesString.split(",")); // 将逗号分隔的字符串转换为列表
            return new UserEntity(
                    rs.getString("username"),
                    rs.getString("password"),
                    roles
            );
登录后复制

以上就是在 Spring Security 6 中集成外部数据库进行用户认证的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号