具有LDAP身份验证的自定义权

时间:2016-05-03 14:50:53

标签: java spring spring-security spring-boot

我发现很少有Spring XML配置示例,用于使用LDAP登录并使用自定义方法而不是通过LDAP配置登录用户的权限。 不幸的是,我找不到任何带注释的Spring Boot示例。

在我们的例子中,有一个中央LDAP存储库,其中存储了用户的用户名和密码,但用户的组不存储在那里。

我感谢任何例子或参考。 提前谢谢。

2 个答案:

答案 0 :(得分:2)

您可以在下面找到我们在项目中的最终实施。 基本流程是:

a)在身份验证期间检查用户ID和角色。如果未定义用户或没有应用程序的角色,请不要对用户进行身份验证。

b)如果用户通过了数据库检查,请继续使用ldap身份验证。

c)将来自数据库的角色与在应用程序期间使用的ldap合并。

public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter implements InitializingBean {
...
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
  .authenticationProvider(this.ldapAndDatabaseAuthenticationProvider());
}

@Bean(name="ldapAuthenticationProvider")
public AuthenticationProvider ldapAndDatabaseAuthenticationProvider(){
  LdapUserDetailsMapper userDetailsMapper = new LdapUserDetailsMapper();
  userDetailsMapper.setRoleAttributes(new String[]{"groupMembership"});

  LdapAndDatabaseAuthenticationProvider provider = 
      new LdapAndDatabaseAuthenticationProvider(this.ldapAuthenticator(), this.ldapAuthoritiesPopulator());
  provider.setUserDetailsContextMapper(userDetailsMapper);

  return provider;
}

@Bean( name = "ldapAuthoritiesPopulator" )
public LdapAndDatabaseAuthoritiesPopulator ldapAuthoritiesPopulator(){
  return new LdapAndDatabaseAuthoritiesPopulator(this.contextSource(), "");
}

@Bean( name = "ldapAuthenticator" )
public LdapAuthenticator ldapAuthenticator() {

    BindAuthenticator authenticator = new BindAuthenticator(   this.contextSource() );
  authenticator.setUserDnPatterns(new String[]{"cn={0},ou=prod,o=COMP"});

  return authenticator;
}

@Bean( name = "contextSource" )
public DefaultSpringSecurityContextSource contextSource() {

    DefaultSpringSecurityContextSource contextSource =
          new DefaultSpringSecurityContextSource( ldapUrl );
    return contextSource;
}

以下是其他角色populator(LdapAndDatabaseAuthoritiesPopulator)的实现方式。

public class LdapAndDatabaseAuthoritiesPopulator extends DefaultLdapAuthoritiesPopulator{

  public LdapAndDatabaseAuthoritiesPopulator(ContextSource contextSource, String groupSearchBase) {
    super(contextSource, groupSearchBase);
  }

  protected Set<GrantedAuthority> getAdditionalRoles(DirContextOperations user,
      String username) {
    Set<GrantedAuthority> mappedAuthorities = new HashSet<GrantedAuthority>();

    /* Add additional roles from other sources for this user*/
    /* below add is just an example of how to add a role */
    mappedAuthorities.add(
        new GrantedAuthority() { 
          private static final long serialVersionUID = 3618700057662135367L;

          @Override 
          public String getAuthority() { 
            return "ROLE_MYAPP_USER"; //this is just a temporary role we are adding as example. get the roles from database.
          } 

         @Override
         public String toString(){
          return this.getAuthority();
         }
        });


    for (GrantedAuthority granted : mappedAuthorities) {
      log.debug("Authority : {}", granted.getAuthority().toString());
    }

    return mappedAuthorities;
  }

}

以下是自定义Ldap身份验证提供程序(LdapAndDatabaseAuthenticationProvider)如何实现以检查用户是否具有在数据库中定义的访问应用程序所需的角色。如果用户不在数据库中或缺少角色,则身份验证将抛出帐户DisabledException。

franDays还建议使用&#34;自定义身份验证提供商&#34;。我想给他一个功劳。

public class LdapAndDatabaseAuthenticationProvider extends LdapAuthenticationProvider{

  public LdapAndDatabaseAuthenticationProvider(LdapAuthenticator authenticator, LdapAuthoritiesPopulator authoritiesPopulator) {
    super(authenticator, authoritiesPopulator);
  }

  @Override
  protected DirContextOperations doAuthentication(
      UsernamePasswordAuthenticationToken authentication) {

    log.debug("Checking if user <{}> is defined at database to use this application.", authentication.getName());

    // Here is the part we need to check in the database if user has required role to log into the application.
    // After check if user has the role, do nothing, otherwise throw exception like below example.    
    boolean canUserAuthenticate = isActiveUserExist(authentication.getName());
    log.debug("canUserAuthenticate: {}", canUserAuthenticate);

    if (!canUserAuthenticate)
      throw new DisabledException("User does not have access to Application!");

    return super.doAuthentication(authentication);
  }


  private boolean isActiveUserExist(String userId) {

  // Do your logic here are return boolean value...

  }

答案 1 :(得分:1)

您可以实现自己的AuthenticationProvider。 authenticate方法将查询LdapTemplate,并在成功尝试后,然后从存储它们的任何位置查询组。它可能如下所示:

public class CustomAuthenticationProvider implements AuthenticationProvider {

  private LdapTemplate ldapTemplate;
  private UserRepository userRepository;

  @Override
  public Authentication authenticate(Authentication authentication) throws AuthenticationException {
      String username = (String) authentication.getPrincipal();
      boolean success = ldapTemplate.authenticate(...);
      if (!success) {
          throw new BadCredentialsException("Wrong username or password");
      }
      User user = userRepository.findByUsername(username); 
      if (user == null) {
          throw new BadCredentialsException("Username not known by the application");
      }
      return new CustomAuthentication(username, user.getRoles());
  }
}

我省略了LdapTemplate的初始化,因为它取决于你的情况的具体情况。您返回的Authentication对象也需要实现一个类,并允许通过传递用户名和密码来构建实例。

如果您需要有关如何使用java配置注册auth提供程序的指导,这篇文章可能有所帮助:Custom Authentication provider with Spring Security and Java Config