当前位置:  开发笔记 > 编程语言 > 正文

如何使用Spring Security对Active Directory服务器进行身份验证?

如何解决《如何使用SpringSecurity对ActiveDirectory服务器进行身份验证?》经验,为你挑选了2个好方法。

我正在编写一个需要用户登录的Spring Web应用程序.我的公司有一个Active Directory服务器,我想为此目的使用它.但是,我在使用Spring Security连接服务器时遇到问题.

我正在使用Spring 2.5.5和Spring Security 2.0.3以及Java 1.6.

如果我将LDAP URL更改为错误的IP地址,它不会抛出异常或任何东西,所以我想知道它是否甚至尝试连接到服务器开始.

尽管Web应用程序启动得很好,但我在登录页面中输入的任何信息都会被拒绝.我以前使用过InMemoryDaoImpl,它工作正常,所以我的应用程序的其余部分似乎都配置正确.

这是我与安全相关的bean:

  
    
      
        
        
          
            CN={0},OU=SBSUsers,OU=Users,OU=MyBusiness,DC=Acme,DC=com
          
        
      
    
  

  
    
  

  
    
  

delfuego.. 36

我做了同样的敲击 - 我的头撞墙体验,最后编写了一个自定义身份验证提供程序,对Active Directory服务器执行LDAP查询.

所以我的安全相关的bean是:


    



    
    



    
    

然后是LdapAuthenticationProvider类:

/**
 * Custom Spring Security authentication provider which tries to bind to an LDAP server with
 * the passed-in credentials; of note, when used with the custom {@link LdapAuthenticatorImpl},
 * does not require an LDAP username and password for initial binding.
 * 
 * @author Jason
 */
public class LdapAuthenticationProvider implements AuthenticationProvider {

    private LdapAuthenticator authenticator;

    public Authentication authenticate(Authentication auth) throws AuthenticationException {

        // Authenticate, using the passed-in credentials.
        DirContextOperations authAdapter = authenticator.authenticate(auth);

        // Creating an LdapAuthenticationToken (rather than using the existing Authentication
        // object) allows us to add the already-created LDAP context for our app to use later.
        LdapAuthenticationToken ldapAuth = new LdapAuthenticationToken(auth, "ROLE_USER");
        InitialLdapContext ldapContext = (InitialLdapContext) authAdapter
                .getObjectAttribute("ldapContext");
        if (ldapContext != null) {
            ldapAuth.setContext(ldapContext);
        }

        return ldapAuth;
    }

    public boolean supports(Class clazz) {
        return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(clazz));
    }

    public LdapAuthenticator getAuthenticator() {
        return authenticator;
    }

    public void setAuthenticator(LdapAuthenticator authenticator) {
        this.authenticator = authenticator;
    }

}

然后是LdapAuthenticatorImpl类:

/**
 * Custom Spring Security LDAP authenticator which tries to bind to an LDAP server using the
 * passed-in credentials; does not require "master" credentials for an
 * initial bind prior to searching for the passed-in username.
 * 
 * @author Jason
 */
public class LdapAuthenticatorImpl implements LdapAuthenticator {

    private DefaultSpringSecurityContextSource contextFactory;
    private String principalPrefix = "";

    public DirContextOperations authenticate(Authentication authentication) {

        // Grab the username and password out of the authentication object.
        String principal = principalPrefix + authentication.getName();
        String password = "";
        if (authentication.getCredentials() != null) {
            password = authentication.getCredentials().toString();
        }

        // If we have a valid username and password, try to authenticate.
        if (!("".equals(principal.trim())) && !("".equals(password.trim()))) {
            InitialLdapContext ldapContext = (InitialLdapContext) contextFactory
                    .getReadWriteContext(principal, password);

            // We need to pass the context back out, so that the auth provider can add it to the
            // Authentication object.
            DirContextOperations authAdapter = new DirContextAdapter();
            authAdapter.addAttributeValue("ldapContext", ldapContext);

            return authAdapter;
        } else {
            throw new BadCredentialsException("Blank username and/or password!");
        }
    }

    /**
     * Since the InitialLdapContext that's stored as a property of an LdapAuthenticationToken is
     * transient (because it isn't Serializable), we need some way to recreate the
     * InitialLdapContext if it's null (e.g., if the LdapAuthenticationToken has been serialized
     * and deserialized). This is that mechanism.
     * 
     * @param authenticator
     *          the LdapAuthenticator instance from your application's context
     * @param auth
     *          the LdapAuthenticationToken in which to recreate the InitialLdapContext
     * @return
     */
    static public InitialLdapContext recreateLdapContext(LdapAuthenticator authenticator,
            LdapAuthenticationToken auth) {
        DirContextOperations authAdapter = authenticator.authenticate(auth);
        InitialLdapContext context = (InitialLdapContext) authAdapter
                .getObjectAttribute("ldapContext");
        auth.setContext(context);
        return context;
    }

    public DefaultSpringSecurityContextSource getContextFactory() {
        return contextFactory;
    }

    /**
     * Set the context factory to use for generating a new LDAP context.
     * 
     * @param contextFactory
     */
    public void setContextFactory(DefaultSpringSecurityContextSource contextFactory) {
        this.contextFactory = contextFactory;
    }

    public String getPrincipalPrefix() {
        return principalPrefix;
    }

    /**
     * Set the string to be prepended to all principal names prior to attempting authentication
     * against the LDAP server.  (For example, if the Active Directory wants the domain-name-plus
     * backslash prepended, use this.)
     * 
     * @param principalPrefix
     */
    public void setPrincipalPrefix(String principalPrefix) {
        if (principalPrefix != null) {
            this.principalPrefix = principalPrefix;
        } else {
            this.principalPrefix = "";
        }
    }

}

最后,LdapAuthenticationToken类:

/**
 * 

* Authentication token to use when an app needs further access to the LDAP context used to * authenticate the user. *

* *

* When this is the Authentication object stored in the Spring Security context, an application * can retrieve the current LDAP context thusly: *

* *
 * LdapAuthenticationToken ldapAuth = (LdapAuthenticationToken) SecurityContextHolder
 *      .getContext().getAuthentication();
 * InitialLdapContext ldapContext = ldapAuth.getContext();
 * 
* * @author Jason * */ public class LdapAuthenticationToken extends AbstractAuthenticationToken { private static final long serialVersionUID = -5040340622950665401L; private Authentication auth; transient private InitialLdapContext context; private List authorities = new ArrayList(); /** * Construct a new LdapAuthenticationToken, using an existing Authentication object and * granting all users a default authority. * * @param auth * @param defaultAuthority */ public LdapAuthenticationToken(Authentication auth, GrantedAuthority defaultAuthority) { this.auth = auth; if (auth.getAuthorities() != null) { this.authorities.addAll(Arrays.asList(auth.getAuthorities())); } if (defaultAuthority != null) { this.authorities.add(defaultAuthority); } super.setAuthenticated(true); } /** * Construct a new LdapAuthenticationToken, using an existing Authentication object and * granting all users a default authority. * * @param auth * @param defaultAuthority */ public LdapAuthenticationToken(Authentication auth, String defaultAuthority) { this(auth, new GrantedAuthorityImpl(defaultAuthority)); } public GrantedAuthority[] getAuthorities() { GrantedAuthority[] authoritiesArray = this.authorities.toArray(new GrantedAuthority[0]); return authoritiesArray; } public void addAuthority(GrantedAuthority authority) { this.authorities.add(authority); } public Object getCredentials() { return auth.getCredentials(); } public Object getPrincipal() { return auth.getPrincipal(); } /** * Retrieve the LDAP context attached to this user's authentication object. * * @return the LDAP context */ public InitialLdapContext getContext() { return context; } /** * Attach an LDAP context to this user's authentication object. * * @param context * the LDAP context */ public void setContext(InitialLdapContext context) { this.context = context; } }

您会注意到那里有一些您可能不需要的位.

例如,我的应用程序需要保留已成功登录的LDAP上下文,以供用户在登录后进一步使用 - 该应用程序的目的是让用户通过其AD凭据登录,然后执行其他与AD相关的功能.因此,我有一个自定义身份验证令牌LdapAuthenticationToken,我传递(而不是Spring的默认身份验证令牌),它允许我附加LDAP上下文.在LdapAuthenticationProvider.authenticate()中,我创建了该令牌并将其传回去; 在LdapAuthenticatorImpl.authenticate()中,我将登录的上下文附加到返回对象,以便可以将其添加到用户的Spring身份验证对象中.

此外,在LdapAuthenticationProvider.authenticate()中,我为所有登录用户分配了ROLE_USER角色 - 这就是让我在intercept-url元素中测试该角色的原因.您希望将此匹配设置为您要测试的任何角色,甚至根据Active Directory组或其他任何角色分配角色.

最后,还有一个推论,我实现LdapAuthenticationProvider.authenticate()的方式为所有用户提供了有效的AD帐户ROLE_USER角色.显然,在这种方法中,可以对用户进行进一步的测试(即是在一个特定的AD组的用户?),甚至授予用户在访问之前分配角色对于一些条件的方式,甚至是测试所有.



1> delfuego..:

我做了同样的敲击 - 我的头撞墙体验,最后编写了一个自定义身份验证提供程序,对Active Directory服务器执行LDAP查询.

所以我的安全相关的bean是:


    



    
    



    
    

然后是LdapAuthenticationProvider类:

/**
 * Custom Spring Security authentication provider which tries to bind to an LDAP server with
 * the passed-in credentials; of note, when used with the custom {@link LdapAuthenticatorImpl},
 * does not require an LDAP username and password for initial binding.
 * 
 * @author Jason
 */
public class LdapAuthenticationProvider implements AuthenticationProvider {

    private LdapAuthenticator authenticator;

    public Authentication authenticate(Authentication auth) throws AuthenticationException {

        // Authenticate, using the passed-in credentials.
        DirContextOperations authAdapter = authenticator.authenticate(auth);

        // Creating an LdapAuthenticationToken (rather than using the existing Authentication
        // object) allows us to add the already-created LDAP context for our app to use later.
        LdapAuthenticationToken ldapAuth = new LdapAuthenticationToken(auth, "ROLE_USER");
        InitialLdapContext ldapContext = (InitialLdapContext) authAdapter
                .getObjectAttribute("ldapContext");
        if (ldapContext != null) {
            ldapAuth.setContext(ldapContext);
        }

        return ldapAuth;
    }

    public boolean supports(Class clazz) {
        return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(clazz));
    }

    public LdapAuthenticator getAuthenticator() {
        return authenticator;
    }

    public void setAuthenticator(LdapAuthenticator authenticator) {
        this.authenticator = authenticator;
    }

}

然后是LdapAuthenticatorImpl类:

/**
 * Custom Spring Security LDAP authenticator which tries to bind to an LDAP server using the
 * passed-in credentials; does not require "master" credentials for an
 * initial bind prior to searching for the passed-in username.
 * 
 * @author Jason
 */
public class LdapAuthenticatorImpl implements LdapAuthenticator {

    private DefaultSpringSecurityContextSource contextFactory;
    private String principalPrefix = "";

    public DirContextOperations authenticate(Authentication authentication) {

        // Grab the username and password out of the authentication object.
        String principal = principalPrefix + authentication.getName();
        String password = "";
        if (authentication.getCredentials() != null) {
            password = authentication.getCredentials().toString();
        }

        // If we have a valid username and password, try to authenticate.
        if (!("".equals(principal.trim())) && !("".equals(password.trim()))) {
            InitialLdapContext ldapContext = (InitialLdapContext) contextFactory
                    .getReadWriteContext(principal, password);

            // We need to pass the context back out, so that the auth provider can add it to the
            // Authentication object.
            DirContextOperations authAdapter = new DirContextAdapter();
            authAdapter.addAttributeValue("ldapContext", ldapContext);

            return authAdapter;
        } else {
            throw new BadCredentialsException("Blank username and/or password!");
        }
    }

    /**
     * Since the InitialLdapContext that's stored as a property of an LdapAuthenticationToken is
     * transient (because it isn't Serializable), we need some way to recreate the
     * InitialLdapContext if it's null (e.g., if the LdapAuthenticationToken has been serialized
     * and deserialized). This is that mechanism.
     * 
     * @param authenticator
     *          the LdapAuthenticator instance from your application's context
     * @param auth
     *          the LdapAuthenticationToken in which to recreate the InitialLdapContext
     * @return
     */
    static public InitialLdapContext recreateLdapContext(LdapAuthenticator authenticator,
            LdapAuthenticationToken auth) {
        DirContextOperations authAdapter = authenticator.authenticate(auth);
        InitialLdapContext context = (InitialLdapContext) authAdapter
                .getObjectAttribute("ldapContext");
        auth.setContext(context);
        return context;
    }

    public DefaultSpringSecurityContextSource getContextFactory() {
        return contextFactory;
    }

    /**
     * Set the context factory to use for generating a new LDAP context.
     * 
     * @param contextFactory
     */
    public void setContextFactory(DefaultSpringSecurityContextSource contextFactory) {
        this.contextFactory = contextFactory;
    }

    public String getPrincipalPrefix() {
        return principalPrefix;
    }

    /**
     * Set the string to be prepended to all principal names prior to attempting authentication
     * against the LDAP server.  (For example, if the Active Directory wants the domain-name-plus
     * backslash prepended, use this.)
     * 
     * @param principalPrefix
     */
    public void setPrincipalPrefix(String principalPrefix) {
        if (principalPrefix != null) {
            this.principalPrefix = principalPrefix;
        } else {
            this.principalPrefix = "";
        }
    }

}

最后,LdapAuthenticationToken类:

/**
 * 

* Authentication token to use when an app needs further access to the LDAP context used to * authenticate the user. *

* *

* When this is the Authentication object stored in the Spring Security context, an application * can retrieve the current LDAP context thusly: *

* *
 * LdapAuthenticationToken ldapAuth = (LdapAuthenticationToken) SecurityContextHolder
 *      .getContext().getAuthentication();
 * InitialLdapContext ldapContext = ldapAuth.getContext();
 * 
* * @author Jason * */ public class LdapAuthenticationToken extends AbstractAuthenticationToken { private static final long serialVersionUID = -5040340622950665401L; private Authentication auth; transient private InitialLdapContext context; private List authorities = new ArrayList(); /** * Construct a new LdapAuthenticationToken, using an existing Authentication object and * granting all users a default authority. * * @param auth * @param defaultAuthority */ public LdapAuthenticationToken(Authentication auth, GrantedAuthority defaultAuthority) { this.auth = auth; if (auth.getAuthorities() != null) { this.authorities.addAll(Arrays.asList(auth.getAuthorities())); } if (defaultAuthority != null) { this.authorities.add(defaultAuthority); } super.setAuthenticated(true); } /** * Construct a new LdapAuthenticationToken, using an existing Authentication object and * granting all users a default authority. * * @param auth * @param defaultAuthority */ public LdapAuthenticationToken(Authentication auth, String defaultAuthority) { this(auth, new GrantedAuthorityImpl(defaultAuthority)); } public GrantedAuthority[] getAuthorities() { GrantedAuthority[] authoritiesArray = this.authorities.toArray(new GrantedAuthority[0]); return authoritiesArray; } public void addAuthority(GrantedAuthority authority) { this.authorities.add(authority); } public Object getCredentials() { return auth.getCredentials(); } public Object getPrincipal() { return auth.getPrincipal(); } /** * Retrieve the LDAP context attached to this user's authentication object. * * @return the LDAP context */ public InitialLdapContext getContext() { return context; } /** * Attach an LDAP context to this user's authentication object. * * @param context * the LDAP context */ public void setContext(InitialLdapContext context) { this.context = context; } }

您会注意到那里有一些您可能不需要的位.

例如,我的应用程序需要保留已成功登录的LDAP上下文,以供用户在登录后进一步使用 - 该应用程序的目的是让用户通过其AD凭据登录,然后执行其他与AD相关的功能.因此,我有一个自定义身份验证令牌LdapAuthenticationToken,我传递(而不是Spring的默认身份验证令牌),它允许我附加LDAP上下文.在LdapAuthenticationProvider.authenticate()中,我创建了该令牌并将其传回去; 在LdapAuthenticatorImpl.authenticate()中,我将登录的上下文附加到返回对象,以便可以将其添加到用户的Spring身份验证对象中.

此外,在LdapAuthenticationProvider.authenticate()中,我为所有登录用户分配了ROLE_USER角色 - 这就是让我在intercept-url元素中测试该角色的原因.您希望将此匹配设置为您要测试的任何角色,甚至根据Active Directory组或其他任何角色分配角色.

最后,还有一个推论,我实现LdapAuthenticationProvider.authenticate()的方式为所有用户提供了有效的AD帐户ROLE_USER角色.显然,在这种方法中,可以对用户进行进一步的测试(即是在一个特定的AD组的用户?),甚至授予用户在访问之前分配角色对于一些条件的方式,甚至是测试所有.



2> Shaun the Sh..:

作为参考,Spring Security 3.1具有专门用于Active Directory的身份验证提供程序.


+1这应该是最佳答案。根据经验,我可以说,如果您使用LdapAuthenticationProvider针对AD进行身份验证,那么您的生活将会非常艰难。在现代版本的Spring中,您可以用不到5行代码来完成所需的工作。请参阅上文Luke提供的链接。我在下面的回答中也提供了详细信息。
推荐阅读
ar_wen2402851455
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有