首页 > 解决方案 > 使用 ActiveDirectoryLdapAuthenticationProvider 时检索 UserDetailsManager

问题描述

我希望我的应用程序的用户能够更改他们的密码。后台的身份验证提供程序是 Active Directory 服务器。

身份验证本身正在工作,尽管我在检索UserDetailsManager允许我更改用户密码的实例时遇到问题。

这是我的安全配置:

  @Override
  protected void configure(AuthenticationManagerBuilder authManagerBuilder) {
    authManagerBuilder.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
  }

  @Bean
  public AuthenticationManager authenticationManager(
      AuthenticationProvider authenticationProvider) {
    return new ProviderManager(Collections.singletonList(authenticationProvider));
  }

  @Bean
  public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
    LOGGER.info("AD provider for domain {} on {}:{}", adDomain, adHost, adPort);
    ActiveDirectoryLdapAuthenticationProvider provider =
        new ActiveDirectoryLdapAuthenticationProvider(adDomain, "ldap://" + adHost + ":" + adPort);
    provider.setConvertSubErrorCodesToExceptions(true);
    provider.setUseAuthenticationRequestCredentials(true);
    provider.setUserDetailsContextMapper(userContextMapper);

    return provider;
  }

  @Bean
  public AuthenticationFailureHandler authenticationFailureHandler() {
    return new CustomAuthenticationFailureHandler();
  }

  @Bean("adUserDetailsManager")
  public UserDetailsManager getUserDetailsManager(ContextSource contextSource) {
    return new LdapUserDetailsManager(contextSource);
  }

在应该处理密码更改的控制器中,我有以下代码:

  private final UserDetailsManager userDetailsManager;

  public AccountController(
      @Qualifier("adUserDetailsManager") UserDetailsManager userDetailsManager) {
    this.userDetailsManager = userDetailsManager;
  }

  @PostMapping("/account")
  public ModelAndView changePassword(
      ModelAndView modelAndView,
      @RequestParam(required = true) String oldPassword,
      @RequestParam(required = true) String newPassword,
      @RequestParam(required = true) String confirmation) {
    modelAndView.setViewName("account");

    if (!StringUtils.equals(newPassword, confirmation)) {
      throw new IllegalArgumentException("Passwords don't match");
    }

    userDetailsManager.changePassword(oldPassword, newPassword);

    return modelAndView;
  }

应用程序启动正常(没有依赖问题),但是一旦我运行该changePassword方法,我就会得到一个异常,即无法访问 localhost 下的服务器:

localhost:389 [Root exception is java.net.ConnectException: Connection refused: connect]

似乎ContextSource注入到我的getUserDetailsManager方法中的是默认的指向 localhost,它不是来自我的 Active Directory 提供程序的实际 LDAP 连接。我在 ActiveDirectoryLdapAuthenticationProvider 上找不到任何可以给我一个实例UserDetailsManager或至少为我提供ContextSourceAD 连接使用的东西的东西,所以我可以连接我自己的LdapUserDetailsManager.

标签: javaspring-securityactive-directory

解决方案


  1. 您提供的是您自己的AuthenticationManagerBuilder,因此未正确配置上下文源。即,如果您使用的不是您自己的,则类似于
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .ldapAuthentication()
                .userDnPatterns("uid={0},ou=people")
                .groupSearchBase("ou=groups")
                .contextSource()
                    .url("ldap://localhost:8389/dc=springframework,dc=org")
                    .and()
  1. 由于您使用的是自己的实现并且contextSource配置正确,因此您必须使用以下内容:
  • 选项1:
    @Bean("adUserDetailsManager")
    public UserDetailsManager getUserDetailsManager() {
        String providerUrl = "your ldap url here";
        DefaultSpringSecurityContextSource contextSource 
                = new DefaultSpringSecurityContextSource(providerUrl);
        contextSource.afterPropertiesSet();
        return new LdapUserDetailsManager(contextSource);
    }
  • 选项 2:
    @Bean
    @Primary
    public ContextSource contextSource() {
        String providerUrl = "your ldap url here";
        return new DefaultSpringSecurityContextSource(providerUrl);
    }
    
    @Bean("adUserDetailsManager")
    public UserDetailsManager getUserDetailsManager(ContextSource contextSource) {
        return new LdapUserDetailsManager(contextSource);
    }

推荐阅读