如何为多个身份验证提供程序提供 Spring Security 的 java 配置

Dmi*_*hko 2 java spring spring-mvc spring-security

我将有关用户的信息存储在单独的表所有者、员工、用户中,我正在尝试在 Spring Security 中使用 java 配置。

我为每种用户类型创建了三个不同的身份验证提供程序,但只有用户提供程序被触发。我已经阅读了 spring 安全文档,唯一的方法似乎是创建具有多个从 WebSecurityConfigurerAdapter 扩展的嵌入式类的类,但我不想这样做,因为它需要大量重复代码,有没有另一种方式

我尝试使用简单的 userDetailService 在其中向数据库中的所有表发送请求,但仍然没有结果,只有一个查询正在执行而什么也没有,我得到的唯一响应是:

2016-02-09 23:06:25.976 DEBUG 8780 --- [nio-8080-exec-1] .saDefaultAuthenticationEventPublisher:未找到异常 org.springframework.security.authentication.InternalAuthenticationServiceException 的事件

2016-02-09 23:06:25.976 DEBUG 8780 --- [nio-8080-exec-1] osswawww.BasicAuthenticationFilter :身份验证请求失败:org.springframework.security.authentication.InternalAuthenticationServiceException:找不到实体进行查询;嵌套异常是 javax.persistence.NoResultException: No entity found for query

但我从不抛出任何异常!!最奇怪的是,我可以在调试器中看到在 em.createQuery(..).getSingleResult().. 之后执行如何迅速停止,仅此而已!没有return语句也没有什么异常,wtf!!

这是我当前配置的一部分:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth
            .authenticationProvider(createAuthenticationProvider(employeeDetailService()))
            .authenticationProvider(createAuthenticationProvider(ownerDetailsService()))
            .authenticationProvider(createAuthenticationProvider(userDetailsService()));
}
 @Bean
    public OwnerDetailsService ownerDetailsService() {
        return new OwnerDetailsService();
    }

    @Bean
    public EmployeeDetailServiceImpl employeeDetailService() {
        return new EmployeeDetailServiceImpl();
    }

    @Bean
    public UserDetailsServiceImpl userDetailsService() {
        return new UserDetailsServiceImpl();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(6);
    }

    @Bean
    public AuthenticationSuccessHandler authenticationSuccessHandler() {
        return new MySimpleUrlAuthenticationSuccessHendler();
    }



    private AuthenticationProvider createAuthenticationProvider(UserDetailsService service) {
    DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
    provider.setUserDetailsService(service);
    provider.setPasswordEncoder(passwordEncoder());
    provider.setHideUserNotFoundExceptions(true);
    return provider;
}
Run Code Online (Sandbox Code Playgroud)

用户详情服务:

  @Service
    public abstract class CustomUserDetailService implements UserDetailsService{

        @Autowired
        IDBBean dao;

        protected CustomUserDetails getUser(GetUserByNameFunction function, String name) {
            return createUser(function.get(name));
        }

        protected CustomUserDetails createUser(Authenticational user) {
            return new CustomUserDetails(user, getAuthorities(user.getAuthority()));
        }

        protected List<GrantedAuthority> getAuthorities(String authority) {
            return Collections.singletonList(new SimpleGrantedAuthority(authority));
        }
    }
Run Code Online (Sandbox Code Playgroud)

实现

    public class EmployeeDetailServiceImpl extends CustomUserDetailService {

        @Override
        public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
            return super.getUser(dao::getEmployeeByEmail, email);
        }
    }

    public class OwnerDetailsService extends CustomUserDetailService {

        @Override
        public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
            return super.getUser(dao::getOwnerByEmail, email);
        }
    }

public class UserDetailsServiceImpl extends CustomUserDetailService {


    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        return super.getUser(dao::getUserByEmail, userName);
    }
}
Run Code Online (Sandbox Code Playgroud)

自定义用户详细信息:

private Long id;
    private String userEmail;


    public CustomUserDetails(Authenticational user,
                             Collection<? extends GrantedAuthority> authorities) {
        super(
                user.getName(),
                user.getPassword().toLowerCase(),
                user.isEnabled(),
                true,
                true,
                true,
                authorities);
        upadateValues(user);
    }

    private void upadateValues(Authenticational user) {
        this.id = user.getId();
        this.userEmail = user.getEmail();
    }
Run Code Online (Sandbox Code Playgroud)

小智 5

只是为了澄清另一个答案中的一些内容:

您的身份验证提供程序存储在ProviderManager内的列表中,该列表通过它们迭代您的身份验证请求。如果您的身份验证提供程序抛出 AuthenticationException(BadCredentialsException extends AuthenticationException),那么 ProviderManager 将尝试另一个提供程序。如果您设置了hideUserNotFoundExceptions属性,那么它也会包装并忽略 UsernameNotFoundException 并在这种情况下尝试另一个提供程序。

如果我是你,我会首先在 ProviderManager 的身份验证方法中放置一个调试点。从那里您可以找出为什么其他身份验证提供程序没有被调用以获取其身份验证方法。

此外,我会考虑只有一个身份验证提供程序和一个 UserDetailsS​​ervice。在我看来,您正在执行许多复杂的并非真正需要的操作,例如将函数传递给您的抽象实现,而您所能做的就是拥有一个 UserDetailsS​​ervice,它会向您的所有 DAO 询问用户。这基本上是您要完成的任务,但减去 2 个身份验证提供程序、减去 1 个抽象类和减去 2 个 UserDetailsS​​ervice 实现。