具有 Active Directory 和数据库角色的 Spring Security

Posted

技术标签:

【中文标题】具有 Active Directory 和数据库角色的 Spring Security【英文标题】:Spring Security with Active Directory and Database roles 【发布时间】:2015-02-18 12:15:38 【问题描述】:

我正在尝试将 Spring Security 与 Active Directory 用户名和密码一起使用。此外,我有一个数据库角色表,它与包含 Active Directory 用户名的用户表有关系(用户表与 Active Directory 无关,它是完全独立的,我只是将用户名放在该表中,这样我就可以将其与角色匹配)

我正在关注这个例子 http://krams915.blogspot.com/2012/01/spring-security-31-implement_1244.html

问题是在CustomUserDetailsS​​ervice类的loadUserByUsername方法中我没看到密码用在哪里。

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException 
        try 
            org.krams.domain.User domainUser = userRepository.findByUsername(username);

            boolean enabled = true;
            boolean accountNonExpired = true;
            boolean credentialsNonExpired = true;
            boolean accountNonLocked = true;

            return new User(
                    domainUser.getUsername(), 
                    domainUser.getPassword().toLowerCase(),
                    enabled,
                    accountNonExpired,
                    credentialsNonExpired,
                    accountNonLocked,
                    getAuthorities(domainUser.getRole().getRole()));

         catch (Exception e) 
            throw new RuntimeException(e);
        
    

据我了解,此行根据数据库 userRepository.findByUsername(username); 验证用户名

但是我怎样才能在那里使用 lpad 验证呢?我可以在这里使用身份验证工具http://www.javaxt.com/Tutorials/Windows/How_to_Authenticate_Users_with_Active_Directory

更新:

我正在尝试让它与 CustomUserDetailsContextMapper 一起工作

XML

<authentication-manager >
    <authentication-provider ref="ldapActiveDirectoryAuthProvider" />
</authentication-manager>

<beans:bean id="ldapActiveDirectoryAuthProvider" 
        class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
    <beans:constructor-arg value="domain" />
    <beans:constructor-arg value="ldap://NAME/"/> 
    <beans:property name="userDetailsContextMapper" ref="tdrUserDetailsContextMapper"/>
    <beans:property name="useAuthenticationRequestCredentials" value="true"/>
</beans:bean>

<beans:bean id="tdrUserDetailsContextMapper" class="com.test8.security8.service.CustomUserDetailsContextMapper"/>

自定义类

public class CustomUserDetailsContextMapper implements UserDetailsContextMapper, Serializable

    private static final long serialVersionUID = 3962976258168853984L;

    @Override
    public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authority) 
        String role="admin";
        System.out.println("TEST");
        if(username.equals("usuario"))role="admin";
        else role="user";
        List authList = getAuthorities(role);

        return new User(username, "", true, true, true, true, authList);
    

    private List getAuthorities(String role) 

        List authList = new ArrayList();
        authList.add(new SimpleGrantedAuthority("ROLE_USER"));

        //you can also add different roles here
        //for example, the user is also an admin of the site, then you can add ROLE_ADMIN
        //so that he can view pages that are ROLE_ADMIN specific
        if (role != null && role.trim().length() > 0) 
            if (role.equals("admin")) 
                authList.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
            
        

        return authList;
    

    @Override
    public void mapUserToContext(UserDetails arg0, DirContextAdapter arg1) 
        // TODO Auto-generated method stub      
    

我想我快到了,我没有收到错误,但它似乎无法映射任何用户,System.out.println("TEST");线永远不会被执行。它可能与 URL/域有关吗?我确定 URL 是正确的,因为如果我更改它,我会收到错误消息。

【问题讨论】:

正如方法所建议的那样,您并没有看不到任何东西,这仅用于加载用户名。密码检查在其他地方处理。 在示例中,在此行中获取了一个用户对象:domainUser = userRepository.findByUsername(username)。我想这样做,但要使用 Active Directory 用户 您有没有花时间阅读documentation,它解释了如何使用活动目录?实现自定义UserDetailsContextMapper 以访问用户名并从数据库中检索角色,其余信息可以来自 LDAP 身份验证。 我想我不明白,:(。我试图围绕我指出的示例构建一个解决方案。但是使用 UserDetailsContextMappers 似乎是一种不同的方法。我应该实现 UserDetailsContextMapper 而不是 UserDetailsS​​ervice 和以同样的方式使用它?。 该示例适用于 JPA 而不是 Active Directory。 【参考方案1】:

我最终使用了 M. Deinum 建议的 UserDetailsContextMapper。我还创建了自定义 SpringSecurityLdapTemplate 和 ActiveDirectoryLdapAuthenticationProvider。

在我的自定义 ActiveDirectoryLdapAuthenticationProvider 中,我更改了以下方法:

private LdapContext bindAsUser(String username, String password) 
    // TODO. add DNS lookup based on domain
    final String bindUrl = url;

    Hashtable<String,String> env = new Hashtable<String,String>();
    env.put(Context.SECURITY_AUTHENTICATION, "simple");
    String bindPrincipal = createBindPrincipal(username);
    env.put(Context.SECURITY_PRINCIPAL, bindPrincipal);
    env.put(Context.PROVIDER_URL, bindUrl);
    env.put(Context.SECURITY_CREDENTIALS, password);
    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    env.put(Context.REFERRAL, "follow");

    try 
        return new InitialLdapContext(env, null);
     catch (NamingException e) 
        if ((e instanceof AuthenticationException) || (e instanceof OperationNotSupportedException)) 
            handleBindException(bindPrincipal, e);
            throw badCredentials();
         else 
            throw LdapUtils.convertLdapException(e);
        
    
 

这对我来说很重要:env.put(Context.REFERRAL, "follow");

并在 SpringSecurityLdapTemplate 自定义类中

public static DirContextOperations searchForSingleEntryInternal(DirContext ctx, SearchControls searchControls,
        String base, String filter, Object[] params) throws NamingException 
    final DistinguishedName ctxBaseDn = new DistinguishedName(ctx.getNameInNamespace());
    final DistinguishedName searchBaseDn = new DistinguishedName(base);
    final NamingEnumeration<SearchResult> resultsEnum = ctx.search(searchBaseDn, filter, params, buildControls(searchControls));

   filter, searchControls);
    if (logger.isDebugEnabled()) 
        logger.debug("Searching for entry under DN '" + ctxBaseDn
                + "', base = '" + searchBaseDn + "', filter = '" + filter + "'");
    

    Set<DirContextOperations> results = new HashSet<DirContextOperations>();
    try 

        while (resultsEnum.hasMore()) 


            SearchResult searchResult = resultsEnum.next();

            DirContextAdapter dca = new DirContextAdapter();

            //dca=(DirContextAdapter) searchResult.getObject();
            Assert.notNull(dca, "No object returned by search, DirContext is not correctly configured");

            if (logger.isDebugEnabled()) 
                logger.debug("Found DN: " + dca.getDn());
            
            results.add(dca);
        
     catch (PartialResultException e) 
        LdapUtils.closeEnumeration(resultsEnum);
        logger.info("Ignoring PartialResultException");
    

    if (results.size() == 0) 
        throw new IncorrectResultSizeDataAccessException(1, 0);
    

    if (results.size() > 1) 
        throw new IncorrectResultSizeDataAccessException(1, results.size());
    

    return results.iterator().next();

这对我有用,但这是一个非常特殊的场景。感谢 M. Deinum 为我指明了正确的方向。

【讨论】:

以上是关于具有 Active Directory 和数据库角色的 Spring Security的主要内容,如果未能解决你的问题,请参考以下文章

具有自定义角色的 Shiro Active Directory

Active Directory 真的符合 LDAP 标准吗?

具有错误的Active Directory Ldap凭据的会话/ Redis序列化错误的Spring启动

Azure Active Directory 是不是具有 OAuth/OpenID Connect 令牌自检终结点?

针对 Active Directory 组进行身份验证

具有多个 Active Directory 服务器的 Grails Spring Security LDAP 插件