Spring security实现多SQL表认证

问题描述 投票:0回答:1

我对 Spring Boot 比较陌生,我一直在探索它的身份验证功能。我注意到 Spring Boot 提供了方便的开箱即用身份验证,前提是用户名和角色在 users 和 users_roles 表中提供。然而,我在跨两种不同的用户类型实现身份验证时面临着挑战,这两种用户类型必须分开并存储在不同的表中,因为它们在我的 Spring Boot 应用程序中保存不同类型的数据 SQL 表。

这是这两个表的 DDL(我知道这些表中没有密码字段或用户名字段,它们可以稍后添加,我只是想要一个更大的解决方案。)这是乘客表,这意味着常规的经过身份验证的用户.

CREATE TABLE IF NOT EXISTS passengers (
                            passenger_id INT AUTO_INCREMENT PRIMARY KEY,
                            name VARCHAR(255) NOT NULL,
                            passport_number VARCHAR(15) UNIQUE NOT NULL,
                            nationality VARCHAR(255) NOT NULL,
                            contact_details VARCHAR(255)
);

另一张是员工表,它具有管理员权限。

CREATE TABLE IF NOT EXISTS employees (
                           employee_id INT AUTO_INCREMENT PRIMARY KEY,
                           name VARCHAR(255) NOT NULL,
                           role VARCHAR(100) NOT NULL,
                           contact_info VARCHAR(255),
                           airport_id INT,
                           FOREIGN KEY (airport_id) REFERENCES airports(airport_id)
);

这是我出于演示目的而编写的一个基本示例(这不适用于我在项目中需要的角色)。所以这里我确实需要从两个不同的表中获取不同的用户。我不知道这样的场景的最佳实践是什么。


@Configuration
public class DemoSecurityConfig {

    // add support for JDBC ... no more hardcoded users :-)

    @Bean
    public UserDetailsManager userDetailsManager(DataSource dataSource) {

        JdbcUserDetailsManager jdbcUserDetailsManager = new JdbcUserDetailsManager(dataSource);

        // define query to retrieve a user by username
        jdbcUserDetailsManager.setUsersByUsernameQuery(
                "select user_id, pw, active from members where user_id=?");

        // define query to retrieve the authorities/roles by username
        jdbcUserDetailsManager.setAuthoritiesByUsernameQuery(
                "select user_id, role from roles where user_id=?");

        return jdbcUserDetailsManager;
    }


    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        http.authorizeHttpRequests(configurer ->
                configurer
                        .requestMatchers(HttpMethod.GET, "/api/employees").hasRole("EMPLOYEE")
                        .requestMatchers(HttpMethod.GET, "/api/employees/**").hasRole("EMPLOYEE")
                        .requestMatchers(HttpMethod.POST, "/api/employees").hasRole("MANAGER")
                        .requestMatchers(HttpMethod.PUT, "/api/employees").hasRole("MANAGER")
                        .requestMatchers(HttpMethod.DELETE, "/api/employees/**").hasRole("ADMIN")
        );

        // use HTTP Basic authentication
        http.httpBasic(Customizer.withDefaults());

        // disable Cross Site Request Forgery (CSRF)
        // in general, not required for stateless REST APIs that use POST, PUT, DELETE and/or PATCH
        http.csrf(csrf -> csrf.disable());

        return http.build();
    }
    
}

我一直在考虑仅维护另一个用户表和两个表的 user_roles 表中的必填字段。但后来我想我会遇到另一个问题,我需要一种类型的用户的数据,并且必须从一个或另一个表中获取。

我们正在使用 Spring Data JPA 并为这些表定义了实体。所以我们有扩展 JPARepositoryInterface 的存储库。有人可以提供指导或潜在的解决方案来实现在此上下文中跨多个 SQL 表的身份验证吗?任何示例或指示将不胜感激。

提前谢谢您!

java spring spring-boot spring-security
1个回答
0
投票

在现实世界中,不同的业务用户很可能需要从不同的来源进行身份验证。让我们来看看解决方案:

您可以为每个入口点注册两个不同的

SecurityFilterChain
。我建议你了解Spring Security Architecture

同样,您可以为每个数据源创建两个不同的

UserDetailsService

安全配置

@Configuration
public class SecurityConfiguration {

private final PassengerUserDetailsService passengerUserDetailsService;
private final EmployeeUserDetailsService employeeUserDetailsService;
private final PasswordEncoder passwordEncoder;

public SecurityConfiguration(PassengerUserDetailsService passengerUserDetailsService,
                             EmployeeUserDetailsService employeeUserDetailsService,
                             PasswordEncoder passwordEncoder) {
    this.passengerUserDetailsService = passengerUserDetailsService;
    this.employeeUserDetailsService = employeeUserDetailsService;
    this.passwordEncoder = passwordEncoder;
}


@Bean
@Order(0)
public SecurityFilterChain filterChain0(HttpSecurity httpSecurity) throws Exception {
    httpSecurity
            .securityMatcher("/passenger/**") // this wi
            .authorizeHttpRequests(
                    auth -> auth.
                            requestMatchers(HttpMethod.GET, "/passenger/**").hasRole("PASSENGER")
            )
            .httpBasic(Customizer.withDefaults())
            .csrf(CsrfConfigurer::disable)
            .userDetailsService(passengerUserDetailsService);
    return httpSecurity.build();
}

@Bean
@Order(1)
public SecurityFilterChain filterChain1(HttpSecurity httpSecurity) throws Exception {
    httpSecurity
            .securityMatcher("/employee/**")
            .authorizeHttpRequests(
                    auth -> auth.
                            requestMatchers(HttpMethod.GET, "/employee/**").hasRole("EMPLOYEE")
            )
            .httpBasic(Customizer.withDefaults())
            .csrf(CsrfConfigurer::disable)
            .userDetailsService(employeeUserDetailsService);
    return httpSecurity.build();
 }
}

旅客用户详情服务

@Service
public class PassengerUserDetailsService implements UserDetailsService  {

private final PasswordEncoder passwordEncoder;

PassengerUserDetailsService(PasswordEncoder passwordEncoder) {
    this.passwordEncoder = passwordEncoder;
}

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    User.UserBuilder builder = User.builder();
    builder.username(username);
    builder.password(passwordEncoder.encode(username));

    switch (username) {
        case "passenger":
            builder.roles("PASSENGER");
            break;
        default:
            throw new UsernameNotFoundException("User not found.");
    }
    return builder.build();
 }
}

员工用户详细信息服务

@Service
public class EmployeeUserDetailsService implements UserDetailsService {

private final PasswordEncoder passwordEncoder;

EmployeeUserDetailsService(PasswordEncoder passwordEncoder) {
    this.passwordEncoder = passwordEncoder;
}

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    User.UserBuilder builder = User.builder();
    builder.username(username);
    builder.password(passwordEncoder.encode(username));

    switch (username) {
        case "employee":
            builder.roles("EMPLOYEE");
            break;
        default:
            throw new UsernameNotFoundException("User not found.");
    }
    return builder.build();
 }
}
© www.soinside.com 2019 - 2024. All rights reserved.