首页 > 解决方案 > Spring Security 具有不同用户详细信息的多个 HTTPSecurity 服务在 Spring Boot 中不起作用

问题描述

我有两种类型的用户:应用程序用户和最终用户,我有单独的表格。现在,我想在这两个表上应用安全性。

我为应用程序用户提供了UserDetailsS​​ervice的自定义实现:

@Component("applicationUserDetailsService")
public class ApplicationUserDetailsService implements UserDetailsService {}

而且,我为最终用户提供了另一个UserDetailsS​​ervice自定义实现:

@Component("endUserDetailsService")
public class EndUserDetailsService implements UserDetailsService {}

现在,在下面的代码片段中,我为这两种类型的用户注册了两个端点。我已经注入了UserDetailsS​​ervice的两个实现,并分别为应用程序和最终用户注册了@Overide configure(AuthenticationManagerBuilder auth)方法。

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@Import(SecurityProblemSupport.class)
public class SecurityConfiguration {

// Injected via Constructor Injection
private final EndUserDetailsService endUserDetailsService;

private final ApplicationUserDetailsService applicationUserDetailsService;

@Configuration
@Order(1)
public class ApplicationUserSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
            .antMatchers(HttpMethod.OPTIONS, "/**")
            .antMatchers("/swagger-ui/index.html")
            .antMatchers("/test/**");
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http
            .csrf()
            .disable()
            .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
            .exceptionHandling()
            .authenticationEntryPoint(problemSupport)
            .accessDeniedHandler(problemSupport)
            .and()
            .headers()
            .frameOptions()
            .disable()
            .and()
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .antMatcher("/api/customer/**")
            .authorizeRequests()
            .antMatchers("/api/customer/authenticate").permitAll()
            .antMatchers("/api/customer/**")
            .authenticated()
            .and()
            .apply(securityConfigurerAdapter());
        // @formatter:on
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(endUserDetailsService);
    }
}

//no @Order defaults to last
@Configuration
public class EndUserSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
            .antMatchers(HttpMethod.OPTIONS, "/**")
            .antMatchers("/swagger-ui/index.html")
            .antMatchers("/test/**");
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http
            .csrf()
            .disable()
            .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
            .exceptionHandling()
            .authenticationEntryPoint(problemSupport)
            .accessDeniedHandler(problemSupport)
            .and()
            .headers()
            .frameOptions()
            .disable()
            .and()
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeRequests()
            .antMatchers("/api/authenticate").permitAll()
            .antMatchers("/api/**").authenticated()                               
            .and()
            .apply(securityConfigurerAdapter());
        // @formatter:on
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(applicationUserDetailsService);
    }
}

private JWTConfigurer securityConfigurerAdapter() {
    return new JWTConfigurer(tokenProvider);
}
}

而且,我正在尝试像这样对用户进行身份验证:

//Injected via Constructor Injection
private final AuthenticationManagerBuilder authenticationManagerBuilder;

UsernamePasswordAuthenticationToken authenticationToken =
        new UsernamePasswordAuthenticationToken(loginVM.getUsername(), loginVM.getPassword());
Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);

执行上述代码片段时,我得到空指针异常,因为authenticationManagerBuilder.getObject()返回空值。而且,当我仅在使用UserDetailService实现而@Component("userDetailsService")不是在安全配置中设置UserDetailServiceauth.userDetailsService("...")时使用它时,它可以正常工作,但是通过这种方式,我无法从多个表中实现身份验证。

我想要实现的目标: 简单来说,我希望 spring security 从两个表中对用户进行身份验证。

标签: springspring-bootspring-security

解决方案


requestMatchers()是您需要的调用,因为它允许您通过 URL 隔离适配器:

@Order(1)
@EnableWebSecurity
class EndUserConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .requestMatchers()
                .antMatchers("/api/customer/**")
                .and()
            .authorizeRequests()
                .antMatchers("/**").hasRole("CUSTOMER")
                .and()
            .apply(yourJointConfigurations());
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(endUserDetailsService);
    }
 }

关于AuthenticationManager直接调用,如果您可以依靠现有的过滤器链为您完成工作,那将是理想的。例如,由于您是无状态的,HTTP Basic 可能更适合您,您可以将其应用于两种配置,而不是尝试使用专用/authenticate端点。


推荐阅读