java - 为 Ldap 连接配置 Spring 安全性

标签 java spring spring-security spring-ldap adam

我必须配置 Spring 安全性以通过 LDAP 对用户进行身份验证。 这是经理用户所在的子树:

ldaps://vldp.floal:636/CN=Administration,CN=fdam,DC=fg,DC=local

这就是用户所在的地方:

ldaps://vldp.floal:636/CN=ProxyUsers,CN=fdam,DC=fg,DC=local

所以我使用这个设置:

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{
     auth.ldapAuthentication()
      .contextSource()
        .url("ldaps://vldp.floal:636/DC=fg,DC=local")
        .managerDn("CN=A0XXX32,CN=Administration,CN=fdam,DC=fg,DC=local")
        .managerPassword(password)
      .and()         
      .userSearchBase("CN=ProxyUsers,CN=fdam")     
      .userSearchFilter("(CN={0})")
      .ldapAuthoritiesPopulator(myAuthPopulator);     
}

问题是当我尝试通过用户登录时抛出异常,我收到此错误:

2016-03-25 14:43:14 [http-nio-8086-exec-6] ERROR o.s.s.w.a.UsernamePasswordAuthenticationFilter - An internal error occurred while trying to authenticate the user.
org.springframework.security.authentication.InternalAuthenticationServiceException: [LDAP: error code 32 - 0000208D: NameErr: DSID-031522C9, problem 2001 (NO_OBJECT), data 0, best match of:
    'CN=fdam,DC=fg,DC=local'
 ]; nested exception is javax.naming.NameNotFoundException: [LDAP: error code 32 - 0000208D: NameErr: DSID-031522C9, problem 2001 (NO_OBJECT), data 0, best match of:
    'CN=fdam,DC=fg,DC=local'
 ]; remaining name 'cn=F67XXX7A,cn=ProxyUsers,cn=fdam'
    at org.springframework.security.ldap.authentication.LdapAuthenticationProvider.doAuthentication(LdapAuthenticationProvider.java:207) ~[spring-security-ldap-4.0.2.RELEASE.jar:4.0.2.RELEASE]
    at org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider.authenticate(AbstractLdapAuthenticationProvider.java:82) ~[spring-security-ldap-4.0.2.RELEASE.jar:4.0.2.RELEASE]
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:167) ~[spring-security-core-4.0.2.RELEASE.jar:4.0.2.RELEASE]
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:192) ~[spring-security-core-4.0.2.RELEASE.jar:4.0.2.RELEASE]
    at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:93) ~[spring-security-web-4.0.2.RELEASE.jar:4.0.2.RELEASE]
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217) ~[spring-security-web-4.0.2.RELEASE.jar:4.0.2.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.2.RELEASE.jar:4.0.2.RELEASE]
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:120) [spring-security-web-4.0.2.RELEASE.jar:4.0.2.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.2.RELEASE.jar:4.0.2.RELEASE]
    at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:120) [spring-security-web-4.0.2.RELEASE.jar:4.0.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.1.RELEASE.jar:4.2.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.2.RELEASE.jar:4.0.2.RELEASE]
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64) [spring-security-web-4.0.2.RELEASE.jar:4.0.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.1.RELEASE.jar:4.2.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.2.RELEASE.jar:4.0.2.RELEASE]
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91) [spring-security-web-4.0.2.RELEASE.jar:4.0.2.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.2.RELEASE.jar:4.0.2.RELEASE]
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53) [spring-security-web-4.0.2.RELEASE.jar:4.0.2.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.1.RELEASE.jar:4.2.1.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.2.RELEASE.jar:4.0.2.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213) [spring-security-web-4.0.2.RELEASE.jar:4.0.2.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176) [spring-security-web-4.0.2.RELEASE.jar:4.0.2.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) [spring-web-4.2.1.RELEASE.jar:4.2.1.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262) [spring-web-4.2.1.RELEASE.jar:4.2.1.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) [catalina.jar:8.0.26]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) [catalina.jar:8.0.26]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219) [catalina.jar:8.0.26]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) [catalina.jar:8.0.26]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) [catalina.jar:8.0.26]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142) [catalina.jar:8.0.26]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) [catalina.jar:8.0.26]
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616) [catalina.jar:8.0.26]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) [catalina.jar:8.0.26]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518) [catalina.jar:8.0.26]
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091) [tomcat-coyote.jar:8.0.26]
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:673) [tomcat-coyote.jar:8.0.26]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1526) [tomcat-coyote.jar:8.0.26]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1482) [tomcat-coyote.jar:8.0.26]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_60]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_60]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-util.jar:8.0.26]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_60]
Caused by: org.springframework.ldap.NameNotFoundException: [LDAP: error code 32 - 0000208D: NameErr: DSID-031522C9, problem 2001 (NO_OBJECT), data 0, best match of:
    'CN=fdam,DC=fg,DC=local'
 ]; nested exception is javax.naming.NameNotFoundException: [LDAP: error code 32 - 0000208D: NameErr: DSID-031522C9, problem 2001 (NO_OBJECT), data 0, best match of:
    'CN=fdam,DC=fg,DC=local'
 ]; remaining name 'cn=F67XXX7A,cn=ProxyUsers,cn=fdam'
    at org.springframework.ldap.support.LdapUtils.convertLdapException(LdapUtils.java:183) ~[spring-ldap-core-2.0.2.RELEASE.jar:2.0.2.RELEASE]
    at org.springframework.security.ldap.authentication.BindAuthenticator.bindWithDn(BindAuthenticator.java:148) ~[spring-security-ldap-4.0.2.RELEASE.jar:4.0.2.RELEASE]
    at org.springframework.security.ldap.authentication.BindAuthenticator.authenticate(BindAuthenticator.java:95) ~[spring-security-ldap-4.0.2.RELEASE.jar:4.0.2.RELEASE]
    at org.springframework.security.ldap.authentication.LdapAuthenticationProvider.doAuthentication(LdapAuthenticationProvider.java:189) ~[spring-security-ldap-4.0.2.RELEASE.jar:4.0.2.RELEASE]
    ... 41 common frames omitted
Caused by: javax.naming.NameNotFoundException: [LDAP: error code 32 - 0000208D: NameErr: DSID-031522C9, problem 2001 (NO_OBJECT), data 0, best match of:
    'CN=fdam,DC=fg,DC=local'
 ]
    at com.sun.jndi.ldap.LdapCtx.mapErrorCode(LdapCtx.java:3160) ~[na:1.8.0_60]
    at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:3081) ~[na:1.8.0_60]
    at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2888) ~[na:1.8.0_60]
    at com.sun.jndi.ldap.LdapCtx.c_getAttributes(LdapCtx.java:1329) ~[na:1.8.0_60]
    at com.sun.jndi.toolkit.ctx.ComponentDirContext.p_getAttributes(ComponentDirContext.java:235) ~[na:1.8.0_60]
    at com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.getAttributes(PartialCompositeDirContext.java:141) ~[na:1.8.0_60]
    at javax.naming.directory.InitialDirContext.getAttributes(InitialDirContext.java:152) ~[na:1.8.0_60]
    at org.springframework.security.ldap.authentication.BindAuthenticator.bindWithDn(BindAuthenticator.java:124) ~[spring-security-ldap-4.0.2.RELEASE.jar:4.0.2.RELEASE]
    ... 43 common frames omitted

spring 参数存在问题,因为如果我使用 java 代码对用户进行身份验证,它就可以工作。 我不知道问题出在哪里,你能帮帮我吗?

我进行了调试,这是错误之前的输出:

2016-03-30 11:35:22 [http-nio-8086-exec-6] DEBUG o.s.security.web.FilterChainProxy - /login at position 6 of 12 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
2016-03-30 11:35:22 [http-nio-8086-exec-6] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/login'; against '/login'
2016-03-30 11:35:22 [http-nio-8086-exec-6] DEBUG o.s.s.w.a.UsernamePasswordAuthenticationFilter - Request is to process authentication
2016-03-30 11:35:22 [http-nio-8086-exec-6] DEBUG o.s.s.authentication.ProviderManager - Authentication attempt using org.springframework.security.ldap.authentication.LdapAuthenticationProvider
2016-03-30 11:35:22 [http-nio-8086-exec-6] DEBUG o.s.s.l.a.LdapAuthenticationProvider - Processing authentication request for user: F67XXX7A
2016-03-30 11:35:22 [http-nio-8086-exec-6] DEBUG o.s.s.l.s.FilterBasedLdapUserSearch - Searching for user 'F67XXX7A', with user search [ searchFilter: '(CN={0})', searchBase: 'CN=fdam', scope: subtree, searchTimeLimit: 0, derefLinkFlag: false ]
2016-03-30 11:35:23 [http-nio-8086-exec-6] DEBUG o.s.l.c.s.AbstractContextSource - Got Ldap context on server 'ldaps://vldp.floal:636/dc=fg,dc=local'
2016-03-30 11:35:23 [http-nio-8086-exec-6] DEBUG o.s.s.l.SpringSecurityLdapTemplate - Searching for entry under DN 'dc=fg,dc=local', base = 'cn=fdam', filter = '(CN={0})'
2016-03-30 11:35:23 [http-nio-8086-exec-6] DEBUG o.s.s.l.SpringSecurityLdapTemplate - Found DN: CN=F67XX7A,CN=ProxyUsers,CN=fdam
2016-03-30 11:35:23 [http-nio-8086-exec-6] DEBUG o.s.s.l.a.BindAuthenticator - Attempting to bind as cn=F67XX7A,cn=ProxyUsers,cn=fdam,dc=fg,dc=local
2016-03-30 11:35:23 [http-nio-8086-exec-6] DEBUG o.s.s.l.DefaultSpringSecurityContextSource - Removing pooling flag for user cn=F67XX7A,cn=ProxyUsers,cn=fdam,dc=fg,dc=local
2016-03-30 11:35:23 [http-nio-8086-exec-6] DEBUG o.s.l.c.s.AbstractContextSource - Got Ldap context on server 'ldaps://vldp.floal:636/dc=fg,dc=local'
2016-03-30 11:35:23 [http-nio-8086-exec-6] DEBUG o.s.s.l.a.BindAuthenticator - Retrieving attributes...
2016-03-30 11:35:23 [http-nio-8086-exec-6] ERROR o.s.s.w.a.UsernamePasswordAuthenticationFilter - An internal error occurred while trying to authenticate the user.

PS:如果使用 ldap 浏览器使用 CN=F67XXX7A,CN=ProxyUsers,CN=fdam,DC=fg,DC=local 它会绑定(bind)。

更新:如果我在没有 Spring 登录的情况下使用 java,我需要使用 Spring 并弄清楚如何配置它:

@Override
public void isAuthenticated(String username, String password) throws LdapException{
    if (databaseMatlabClientServices.getByUsersEnabled(username)== null)
        throw new LdapException("User doesn't exist into DART database. Please contact the administrator!");
    String dn="";;
    //First query to retriev DN
    Hashtable<String, Object> ldapEnv = new Hashtable<String, Object>();
    ldapEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    ldapEnv.put(Context.PROVIDER_URL, env.getRequiredProperty(PROPERTY_NAME_LDAP_URL));
//Without authentication        ldapEnv.put(Context.SECURITY_AUTHENTICATION, "none");
    //With authentication to access to LDAP server
    ldapEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
    ldapEnv.put(Context.SECURITY_PRINCIPAL, env.getRequiredProperty(PROPERTY_NAME_LDAP_NAME));
    ldapEnv.put(Context.SECURITY_CREDENTIALS, env.getRequiredProperty(PROPERTY_NAME_LDAP_PASSWORD));
    String[] returnAttribute = {"dn"};
    DirContext ctx = null;
    NamingEnumeration<SearchResult> results = null;
    try {
        ctx = new InitialDirContext(ldapEnv);
        SearchControls controls = new SearchControls();
        controls.setReturningAttributes(returnAttribute);
        controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
// without authentication on local server String filter = "uid=" + username ;
        String filter = "CN=" + username ;
        results = ctx.search(env.getRequiredProperty(PROPERTY_NAME_LDAP_USERSEARCHBASE), filter, controls);
        if (results.hasMore())
            dn = results.nextElement().getNameInNamespace();
        else 
            throw new LdapException("Wrong username. Please retry!");
    } catch (NamingException e) {
        throw new LdapException(e);
    } finally {
        try{
            if (results != null)
                results.close();             
            if (ctx != null) 
                ctx.close();
        }catch(Exception e){
            throw new LdapException(e);
        }
    }

    //Second query to try to access with obtained Dn and given password
    Hashtable<String, Object> authEnv = new Hashtable<String, Object>();
    authEnv.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
    authEnv.put(Context.PROVIDER_URL, env.getRequiredProperty(PROPERTY_NAME_LDAP_URL));
    authEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
    authEnv.put(Context.SECURITY_PRINCIPAL, dn);
    authEnv.put(Context.SECURITY_CREDENTIALS, password);
    DirContext ctx2 = null;
    try {
        ctx2 = new InitialDirContext(authEnv);
    } catch (AuthenticationException authEx) {
        throw new LdapException("Authentication error. Password was wrong");
    } catch(Exception e){
        throw new LdapException(e);
    }finally {
        try{         
            if (ctx2 != null) 
                ctx2.close();
        }catch(Exception e){
            throw new LdapException(e);
        }
    }
}

更新@pczeus 代码: 要设置referral=follow,我使用了以下代码:

DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource("ldaps://vldp.floal:636/");
contextSource.setUserDn("CN=A0XXX32,CN=Administration,CN=fdam,DC=fg,DC=local");
contextSource.setPassword(password);
contextSource.setReferral("follow"); 
contextSource.afterPropertiesSet();

LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> ldapAuthenticationProviderConfigurer = auth.ldapAuthentication();

ldapAuthenticationProviderConfigurer
    .ldapAuthoritiesPopulator(myAuthPopulator)
    .userSearchFilter("(CN={0},CN=ProxyUsers,CN=fdam,DC=fg,DC=local)")
    .userSearchBase("")
    .contextSource(contextSource);

我收到了经典异常:

2016-04-07 09:34:41 [http-nio-8086-exec-4] ERROR o.s.s.w.a.UsernamePasswordAuthenticationFilter - An internal error occurred while trying to authenticate the user.
org.springframework.security.authentication.InternalAuthenticationServiceException: [LDAP: error code 32 - 0000208D: NameErr: DSID-031001E5, problem 2001 (NO_OBJECT), data 0, best match of:
    ''
 ]; nested exception is javax.naming.NameNotFoundException: [LDAP: error code 32 - 0000208D: NameErr: DSID-031001E5, problem 2001 (NO_OBJECT), data 0, best match of:
    ''
 ]; remaining name '/'

如果我添加 .userSearchBase("CN=ProxyUsers,CN=fgadam,DC=fg,DC=local") 我收到了

2016-04-07 10:32:56 [http-nio-8086-exec-4] DEBUG o.s.s.l.s.FilterBasedLdapUserSearch - Searching for user 'F6XXX7A', with user search [ searchFilter: '(CN={0},CN=ProxyUsers,CN=fdam,DC=fg,DC=local)', searchBase: 'CN=ProxyUsers,CN=fdam,DC=fg,DC=local', scope: subtree, searchTimeLimit: 0, derefLinkFlag: false ]
2016-04-07 10:32:57 [http-nio-8086-exec-4] DEBUG o.s.l.c.s.AbstractContextSource - Got Ldap context on server 'ldaps://vldp.floal:636/'
2016-04-07 10:32:57 [http-nio-8086-exec-4] DEBUG o.s.s.l.SpringSecurityLdapTemplate - Searching for entry under DN '', base = 'cn=ProxyUsers,cn=fdam,dc=fg,dc=local', filter = '(CN={0},CN=ProxyUsers,CN=fdam,DC=fg,DC=local)'
2016-04-07 10:32:57 [http-nio-8086-exec-4] DEBUG o.s.s.w.a.UsernamePasswordAuthenticationFilter - Authentication request failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials

其中 DN 是 '',为什么?

最佳答案

In AbstractContextSource (parent of LdapContextSource), the Javadoc for the setBase() method says the following: "Set the base suffix from which all operations should origin. If a base suffix is set, you will not have to (and, indeed, must not) specify the full distinguished names in any operations performed.". Since you specify the full DN for the userDN/filter, hence you must not specify the base.

尝试将 userSearchBase 设置为空字符串 (""),或者将调用全部删除。

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{
     auth.ldapAuthentication()
         .contextSource()
         .url("ldaps://vldp.floal:636/DC=fg,DC=local")
         .managerDn("CN=A0XXX32,CN=Administration,CN=fdam,DC=fg,DC=local")
         .managerPassword(password)
         .and()         
         .userSearchBase("")     
         .userSearchFilter("CN={0},CN=ProxyUsers,CN=fdam,DC=fg,DC=local")
         .ldapAuthoritiesPopulator(myAuthPopulator);     
}

关于java - 为 Ldap 连接配置 Spring 安全性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36305360/

相关文章:

java - 如何使用字符打印锯齿状数组?

java - 使用 Maven 在 Debian 上构建 RPM

java - 自定义 Java Tapestry @IncludeStylesheet 注释

java - 实现默认大小验证的验证消息

java - Spring 安全 : mapping of exceptions thrown inside security filter

java - 以 HTML 形式传递字符串数组并提交给 Java Spring Controller?

java - 如何选择 datetime mysql 类型作为 java.time.LocalDateTime?

java - 强制不同的 Spring Repositories 使用相同的事务

java - 如何在没有 SAML 的情况下在多个 Spring Security Web 应用程序上使用 SSO?

Spring Boot-无效的访问 token 错误