简体   繁体   English

Spring 安全 - 首先验证数据库中的用户,然后再针对 AD 进行身份验证

[英]Spring Security - Verify user in DB first and then authenticate against AD

We have a requirement to verify whether a username exists in database and then authenticate against AD.我们需要验证用户名是否存在于数据库中,然后针对 AD 进行身份验证。 If username doesn't exist application will return error instead of trying to authenticate against AD.如果用户名不存在,应用程序将返回错误,而不是尝试针对 AD 进行身份验证。 I have authenticated against multiple AD's and/or database but I have trouble getting this to work.我已经针对多个 AD 和/或数据库进行了身份验证,但我无法使其正常工作。 Any hints would be helpful.任何提示都会有所帮助。 Thank you谢谢

In my class that extends WebSecurityConfigurerAdapter I tried to play with authenticationProvider where I could verify the existence in DB.在我扩展WebSecurityConfigurerAdapter的 class 中,我尝试使用authenticationProvider来验证数据库中是否存在。 But not sure of what to return so that the authentication could be proceed to LDAP.但不确定要返回什么,以便身份验证可以继续到 LDAP。

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
     .authenticationProvider(authenticationProvider)
    .authenticationEventPublisher(authenticationEventPublisher)
    .ldapAuthentication()
    .....;

} }

I also tried adding a before/after filter but not successful in there either我也尝试添加一个之前/之后的过滤器,但也没有成功

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
   ....
 .and()
 .addFilterBefore(preAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
;

@gandr solution got me thinking, and my final solution was: @gandr 解决方案让我开始思考,我最终的解决方案是:

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
  .authenticationProvider(PreLdapAuthenticationProvider)
   // authenticationEventPublisher not used anymore
  .ldapAuthentication()
  .....;
private static class PreLdapAuthenticationProvider implements AuthenticationProvider {
  @Override
  public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    this.userService.checkUserEnabled((String) authentication.getPrincipal());
    return null;
  }
}
public class UserService {
  public void checkUserEnabled(String username) throws AuthenticationException {
    UserEntity entity = userRepository.findByLogin(username);
    if (entity == null) {
      throw new PreLdapUsernameNotFoundException();
      // normal UsernameNotFoundException extends AuthenticationException
      // and will be caught and ignore, but not if my custom class
      // extends AccountStatusException
    }
    if (!entity.isEnabled()) {
      throw new DisabledException("DisabledException");
    }
  }
}
public class PreLdapUsernameNotFoundException extends AccountStatusException {
  public PreLdapUsernameNotFoundException() {
    super("PreLdapUsernameNotFoundException");
  }
}

Then you can catch PreLdapUsernameNotFoundException and InternalAuthenticationServiceException in然后你可以在中捕获 PreLdapUsernameNotFoundException 和 InternalAuthenticationServiceException

private AuthenticationFailureHandler authenticationFailureHandler() {
  return (request, response, authenticationException) -> {
    if (authenticationException instanceof PreLdapUsernameNotFoundException){
      ...
    } else ...
  }
}

In the filter preAuthenticationFilter the instance of request passed in doFilter() method FirewalledRequest. 在过滤器preAuthenticationFilter中,通过doFilter()方法FirewalledRequest传递的请求实例。 From this instance I am unable to get the username; 从这个实例中,我无法获得用户名; looks like this is by design. 看起来这是设计使然。 If anyone has any advice on how we could retrieve username from the instance of FirewalledRequest please share it here. 如果有人对如何从FirewalledRequest实例检索用户名有任何建议,请在此处共享。 I will give it a try. 我会试一试。

So instead of using the filter I decided to play with the custom AuthenticationProvider. 因此,我决定不使用过滤器,而是使用自定义AuthenticationProvider。 In the AuthenticationProvider implementation under the method authenticate() I return null (and log, notify, etc.) when user exist. 在authenticate()方法下的AuthenticationProvider实现中,当用户存在时,我返回null(并记录,通知等)。 If user doesn't exits I return the same instance of authentication passed. 如果用户不退出,我将返回通过的相同身份验证实例。 This breaks the chain and stops proceeding to authenticating against AD. 这将中断链并停止进行针对AD的身份验证。 Throwing any instance of AuthenticationException doesn't work as Spring security captures this exception and proceeds further (per docs). 抛出AuthenticationException的任何实例均不起作用,因为Spring安全性会捕获此异常并进一步处理(根据文档)。

Here is how the code snippet looks like 这是代码段的样子

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException
{
    Optional<User> user = service.findUserByUsername((String) authentication.getPrincipal());

    if (user.isPresent())
        return null;

    return authentication;
}

Please share any better ideas. 请分享任何更好的想法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM