当前位置: 首页 > 面试题库 >

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

宋瀚海
2023-03-14
问题内容

我正在编写一个要求用户登录的Spring Web应用程序。我公司有一个Active Directory服务器,我想将其用于此目的。但是,我在使用Spring
Security连接服务器时遇到了麻烦。

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

如果我将LDAP URL更改为错误的IP地址,它不会引发异常或任何异常,因此我想知道它是否甚至 试图 连接到服务器。

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

这是我与安全性相关的bean:

  <beans:bean id="ldapAuthProvider" class="org.springframework.security.providers.ldap.LdapAuthenticationProvider">
    <beans:constructor-arg>
      <beans:bean class="org.springframework.security.providers.ldap.authenticator.BindAuthenticator">
        <beans:constructor-arg ref="initialDirContextFactory" />
        <beans:property name="userDnPatterns">
          <beans:list>
            <beans:value>CN={0},OU=SBSUsers,OU=Users,OU=MyBusiness,DC=Acme,DC=com</beans:value>
          </beans:list>
        </beans:property>
      </beans:bean>
    </beans:constructor-arg>
  </beans:bean>

  <beans:bean id="userDetailsService" class="org.springframework.security.userdetails.ldap.LdapUserDetailsManager">
    <beans:constructor-arg ref="initialDirContextFactory" />
  </beans:bean>

  <beans:bean id="initialDirContextFactory" class="org.springframework.security.ldap.DefaultInitialDirContextFactory">
    <beans:constructor-arg value="ldap://192.168.123.456:389/DC=Acme,DC=com" />
  </beans:bean>

问题答案:

我曾遇到过与我相同的经历,最后写了一个自html" target="_blank">定义身份验证提供程序,该程序对Active Directory服务器执行LDAP查询。

因此,与安全性相关的bean是:

<beans:bean id="contextSource"
    class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
    <beans:constructor-arg value="ldap://hostname.queso.com:389/" />
</beans:bean>

<beans:bean id="ldapAuthenticationProvider"
    class="org.queso.ad.service.authentication.LdapAuthenticationProvider">
    <beans:property name="authenticator" ref="ldapAuthenticator" />
    <custom-authentication-provider />
</beans:bean>

<beans:bean id="ldapAuthenticator"
    class="org.queso.ad.service.authentication.LdapAuthenticatorImpl">
    <beans:property name="contextFactory" ref="contextSource" />
    <beans:property name="principalPrefix" value="QUESO\" />
</beans: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 <strong>not</strong> 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 <strong>not</strong> 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类:

/**
 * <p>
 * Authentication token to use when an app needs further access to the LDAP context used to
 * authenticate the user.
 * </p>
 * 
 * <p>
 * When this is the Authentication object stored in the Spring Security context, an application
 * can retrieve the current LDAP context thusly:
 * </p>
 * 
 * <pre>
 * LdapAuthenticationToken ldapAuth = (LdapAuthenticationToken) SecurityContextHolder
 *      .getContext().getAuthentication();
 * InitialLdapContext ldapContext = ldapAuth.getContext();
 * </pre>
 * 
 * @author Jason
 * 
 */
public class LdapAuthenticationToken extends AbstractAuthenticationToken {

    private static final long serialVersionUID = -5040340622950665401L;

    private Authentication auth;
    transient private InitialLdapContext context;
    private List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();

    /**
     * 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角色,这就是让我在我的URL元素中测试该角色的原因。您将要使其与要测试的角色匹配,甚至根据Active
Directory组或其他角色分配角色。

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



 类似资料:
  • 下面是示例代码:https://code.google.com/p/google-api-dotnet-client/wiki/oauth2#service_accounts 授权失败:dotnetOpenAuth.Messaging.ProtocolException:发送直接消息或获取响应时出错。 内部异常是System.net.WebException:远程服务器返回错误:(400)Bad

  • GoogleCredential凭证=newGoogleCredential.Builder(). setTransfer(TRANSPORT). setJsonFactory(JSON_FACTORY). setServiceAccount tId("SOMETHING@developer.gserviceaccount.com"). setServiceAccount tScopes(Bigq

  • 假设我们有以下部分的web安全配置:

  • 我有一个节点项目 我试过的密码- 运行此命令时,当secret_password错误时,它会正确显示,但当它正确时,它只显示一个单词swagger,表示swagger。io链接。 我搜索了一下,但是没有得到任何关于这个的信息或者例子。我错过了什么吗?

  • 我正在开发一个Springboot应用程序,该应用程序需要支持本地(通过基于DAO的提供程序)和多个LDAP服务器(管理配置,存储在数据库中)进行身份验证。 对于单个LDAP提供程序,我的配置方法如下所示: 通过其他类似的帖子,这似乎可以通过创建多个LDAP提供者来实现,Spring security将在每个提供者之间循环,直到找到成功的登录。我将关联的LDAP配置记录作为外键关联到用户表上。 有

  • null 我研究了OAuth2隐式授权,但它要求用户在成功验证后批准/拒绝应用程序。它在我的情况下不起作用,因为我同时拥有应用程序和API。 我查看了OAuth2密码授权,它并不完美,因为我需要公开client_id/client_secret。 我关注OAuth2的原因是因为该API最终将是公开的。 忘记OAuth2,在用户发布用户名/密码时手动生成access_token(在本例中,当API公