Grails - Spring 安全 ldap 活动目录身份验证 - 凭据错误错误

Posted

技术标签:

【中文标题】Grails - Spring 安全 ldap 活动目录身份验证 - 凭据错误错误【英文标题】:Grails - Spring security ldap active directory authentication - bad credentials error 【发布时间】:2019-11-19 07:42:26 【问题描述】:

我正在尝试使用 grails-sporing-security-rest 和 grails-spring-security-ldap 插件对 REST API 实施安全性。

流程应该是这样的,一旦实现了身份验证提供程序,它将使用身份验证机制对用户进行身份验证。

但在我的情况下,我正在尝试验证 JSON 用户名和密码 POST 请求,下面是输出。

如果观察日志,SpringSecurityLDAPTemplate 认证成功,但是当请求进入下一个过滤器 RestAuthenticationFilter 时,它显示 Bad Credentials 错误。这让人有些困惑。 /api/login 是 spring 安全端点,如果认证成功,它将获取用户名和密码,它应该返回适​​当的令牌,但是为什么它又被发送到 RestAuthenticationFilter 呢?

使用正确的用户名和密码:

o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/login'; against '/api/**'
o.s.security.web.FilterChainProxy        : /api/login at position 1 of 7 in additional filter chain; firing Filter: 'SecurityRequestHolderFilter'
o.s.security.web.FilterChainProxy        : /api/login at position 2 of 7 in additional filter chain; firing Filter: 'MutableLogoutFilter'
o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/login'; against '/logoff'
o.s.security.web.FilterChainProxy        : /api/login at position 3 of 7 in additional filter chain; firing Filter: 'RestAuthenticationFilter'
g.p.s.rest.RestAuthenticationFilter      : Actual URI is /api/login; endpoint URL is /api/login
g.p.s.rest.RestAuthenticationFilter      : Applying authentication filter to this request
c.DefaultJsonPayloadCredentialsExtractor : Extracted credentials from JSON payload. Username: xxxxxxx, password: [PROTECTED]
g.p.s.rest.RestAuthenticationFilter      : Trying to authenticate the request
o.s.s.authentication.ProviderManager     : Authentication attempt using org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider
o.s.s.ldap.SpringSecurityLdapTemplate    : Searching for entry under DN '', base = 'dc=xxx,dc=xxx,dc=xxx,dc=xxx', filter = '(&(objectClass=user)(userPrincipalName=0))'
 g.p.s.rest.RestAuthenticationFilter      : Authentication failed: Bad credentials
g.p.s.r.token.bearer.BearerTokenReader   : Looking for bearer token in Authorization header, query string or Form-Encoded body parameter
g.p.s.r.token.bearer.BearerTokenReader   : No token found
g.p.s.r.token.bearer.BearerTokenReader   : Token: null
.BearerTokenAuthenticationFailureHandler : Sending status code 401 and header WWW-Authenticate: Bearer

用户名和密码错误:

o.s.s.authentication.ProviderManager     : Authentication attempt using org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider
ctiveDirectoryLdapAuthenticationProvider : Active Directory authentication failed: Supplied password was invalid
 g.p.s.rest.RestAuthenticationFilter      : Authentication failed: Bad credentials

Resources.Groovy

import myapp.MyUserDetailsContextMapper
import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider

// Place your Spring DSL code here
beans = 
    ldapAuthProvider1(ActiveDirectoryLdapAuthenticationProvider, "xxx" ,"xxxx" ) 
        userDetailsContextMapper = ldapUserDetailsMapper
    
    ldapUserDetailsMapper(MyUserDetailsContextMapper) 

    

application.groovy

grails.plugin.springsecurity.filterChain.chainMap = [
        //Stateless chain
        [
                pattern: '/api/**',
                filters: 'JOINED_FILTERS,-anonymousAuthenticationFilter,-exceptionTranslationFilter,-authenticationProcessingFilter,-securityContextPersistenceFilter,-rememberMeAuthenticationFilter'
        ],

        //Traditional, stateful chain
        [
                pattern: '/stateful/**',
                filters: 'JOINED_FILTERS,-restTokenValidationFilter,-restExceptionTranslationFilter'
        ]
]
grails.plugin.springsecurity.rest.token.storage.jwt.secret="xyz1234dsgsd4546dfhg345745754785756856856785679fghfgj"
grails.plugin.springsecurity.rest.login.active=true
grails.plugin.springsecurity.rest.login.endpointUrl="/api/login"
grails.plugin.springsecurity.rest.login.failureStatusCode=401

application.yml - grails 配置

grails:
    plugin:
        springsecurity:
            providerNames: ["ldapAuthProvider1","anonymousAuthenticationProvider"]
            useSecurityEventListener: true
            ldap:
                search:
                    attributesToReturn: ['mail', 'displayName']
                    filter: '(sAMAccountName=0)'
                    searchSubtree: true
                authorities:
                    retrieveDatabaseRoles:  false
                    ignorePartialResultException: true
                authenticator:
                    useBind: true
                    attributesToReturn: null
                auth:
                    useAuthPassword: true
            rest:
                login:
                    useJsonCredentials: true
                    usernamePropertyName: username
                    passwordPropertyName: password

更新:

进一步解决了问题,并注意到了这一点。

下面的代码在 SpringSecurityLdapTemplate 类和方法 searchForSingleEntryInternal - 来自 spring-security-ldap 4.2.3 版本。从 ActiveDirectoryLdapAuthenticationProvider 类调用它以从 LDAP 获取信息。

final DistinguishedName searchBaseDn = new DistinguishedName(base);
    final NamingEnumeration<SearchResult> resultsEnum = ctx.search(searchBaseDn,
            filter, params, buildControls(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 = (DirContextAdapter) searchResult.getObject();
            Assert.notNull(dca,
                    "No object returned by search, DirContext is not correctly configured");

在这种情况下 searchResult.getObject 返回 null 导致身份验证失败,但我验证我能够从 resultsEnum 获取属性。

非常感谢任何帮助。谢谢。

【问题讨论】:

您可能不应该在这些 sn-ps 中发布您的密码 同意 Ferdz.. 这实际上是虚拟值而不是原始秘密。 这行代码是 ActiveDirectoryAuthenticationProvider 类的一部分,它正在制造麻烦,因为它期望 LdapContext 对象,实际上它作为 null 传递。扩展 AbstractAuthenticationProvider 并创建自定义身份验证处理程序来解决问题。 DirContextAdapter dca = (DirContextAdapter) searchResult.getObject(); 【参考方案1】:

作为 ActiveDirectoryAuthenticationProvider 类的一部分的这行代码正在制造麻烦,因为它期望 LdapContext 对象,实际上它作为 null 传递。扩展 AbstractAuthenticationProvider 并创建自定义身份验证处理程序来解决问题。

DirContextAdapter dca = (DirContextAdapter) searchResult.getObject();

【讨论】:

以上是关于Grails - Spring 安全 ldap 活动目录身份验证 - 凭据错误错误的主要内容,如果未能解决你的问题,请参考以下文章

Grails,Spring Security LDAP 插件

如何使用 spring-security-core-ldap 插件在 grails 中实现 LDAP 身份验证?

Grails spring security LDAP 从 LDAP 基础获取 ROLE

Grails 中带有 Spring Security 的 Gormless LDAP

未分配 Grails Spring Security LDAP“defaultRole”

在带有 CAS 和 LDAP 的 Grails 中使用 Spring Security