使用 LDAP 在 Spring oauth/token 请求上验证客户端 ID 和客户端密码

Posted

技术标签:

【中文标题】使用 LDAP 在 Spring oauth/token 请求上验证客户端 ID 和客户端密码【英文标题】:Authenticate client id and client secret on Spring oauth/token request with LDAP 【发布时间】:2018-05-19 03:53:21 【问题描述】:

我正在研究如何使用 LDAP 对客户端 ID 和客户端密码进行身份验证。

注意:这个 Kotlin 代码...

@Configuration
@EnableAuthorizationServer
class OAuth2AuthorizationServerConfig() : AuthorizationServerConfigurerAdapter() 

我对 Spring 比较陌生,看来这不是我应该尝试的事情?但是,这似乎是一个有用的选择。为什么?因为它允许我将客户端机密管理委托给 LDAP 目录,并且有效地允许我的运维团队更改机密(以某种托管方式)。有了这个,我的应用程序不需要知道秘密。这看起来很整洁?

oauth 端点是基本身份验证——这似乎是 Spring 通过 @EnableAuthorizationServer 注释给我的。对http://somehost/oauth/token 的请求指定grant_type:client_credentials

我创建了代码来获取任意令牌(沙盒)...而我只想指定适用于该客户端的客户端和范围,指定秘密.. .

@Throws(Exception::class)
override fun configure(
        clients: ClientDetailsServiceConfigurer
) 
    // Inlining will create a store per credential entry
    val serviceBuilder = clients.inMemory()
    serviceBuilder.withClient("user").secret("test").scopes("XXX")

我尝试了很多不同的想法来将LDAP Authentication Provider 添加到ProviderManager 中的托管提供程序集中,但到目前为止都失败了。如果我在运行时调试authenticate 方法,我总是只有AnonymousAuthenticationProviderDaoAuthenticationProvider

以下可能表明我缺乏经验,但这是一个示例,请阅读可能的疯狂 - 只是想看看我是否可以注入 LDAPAuthenticationProvider...

@Autowired
lateinit var providerMan: AuthenticationManager

@Throws(Exception::class)
override fun configure(endpoints: AuthorizationServerEndpointsConfigurer) 
    (providerMan as ProviderManager).providers.add(0,
            LdapAuthenticationProvider(
                    PasswordComparisonAuthenticator(PasswordPolicyAwareContextSource("ldap://something"))
            )
    )

因此问题相当简单... 有没有办法添加 LdapAuthenticationProvider 以便我可以使用 LDAP 来验证客户端 ID 和客户端密码?

【问题讨论】:

【参考方案1】:

要在 LDAP 中拥有客户端 ID 和机密,您需要一个基于 LDAP 的 ClientDetailsService 实现,而 Spring Security OAuth 中不存在该实现。也许构建该实现作为到LDAPAuthenticationProvider 的桥梁是可行的,但我不确定这是否是一个好主意。在任何情况下,您都必须自己构建实现。

【讨论】:

如何使用 Ldap 比较操作来验证密码? .....我遇到了障碍 - ... 因为loadClientByClientId(String clientId) 不知道client_secret 我没有办法使用ldap比较操作来验证密码。而PasswordEncoder#matches(...) 方法不知道clientId?还有什么建议吗? 好的,我可以通过将MyClientDetailsService@Component@Autowire 设置为我的AuthorizationServerConfigurerAdapter 来“破解”它。然后保持客户端ID并将MyClientDetailsService传递给我的自定义PasswordEncoder然后在运行时PasswordEncoder可以请求Ldap密码与传递给matchesrawPassword和我从MyClientDetailsService保持的clientId进行比较有点奇怪/狡猾? 我并不是很熟悉所有的内部结构,但是从 JDBC 实现来看,loadClientByClientId(String clientId) 似乎不需要(或不应该)验证密码。 Spring Security 似乎设置了一个专用的身份验证提供程序,它使用一个 ClientDetailUserDetailsS​​ervice 将客户端详细信息包装成用户详细信息,并在加载客户端详细信息后比较密码。【参考方案2】:

我想为社区的答案添加一些详细信息。

多亏了建议,我有了一个可以工作的原型。 我正在使用Apache LDAP API 连接到 LDAP 服务器。

解决方案相当简单 - 只需创建一个 ClientDetailsService,它检索与传递给 loadClientByClientId 的 clientId 匹配的 LDAP 条目并返回一个适当的 ClientDetails 对象(使用 Spring 提供的 BaseClientDetails)。

您还需要创建自定义PasswordEncoder 或设置正确的编码器,如果 Spring 为您提供必要的:

override fun configure(security: AuthorizationServerSecurityConfigurer) 
    security.passwordEncoder(SomePasswordEncoder)

默认是纯文本密码编码器。

【讨论】:

客户端的密码是从哪里得到的?一般来说,您无法从 LDAP 获取密码。 我的理解是用来连接LDAP目录的绑定用户(在我的例子中是AD)必须有密码属性的权限。 所以您使用自定义ClientDetailsService 中的client_secret 作为用户(client_id) 的LDAP 密码?然后您从 LDAP 中获取该用户的密码,对吗?如果密码错误,你在ClientDetailsService中抛出异常,对吧? 不,使用我当前的原型,我只返回带有 client_secret 集的 ClientDetails。 Spring 管道通过 Spring 与 AuthorizationServer 提供的 DaoAuthenticationProvider 进行 client_secret 比较。我已经阅读了更多内容,发现我可能需要考虑使用 LDAP 比较请求来进行密码比较(您的初始评论)。我“认为”这意味着我需要一个自定义 PasswordEncoder 但我还不知道怎么做,因为编码器在运行时没有 clientid :-(.

以上是关于使用 LDAP 在 Spring oauth/token 请求上验证客户端 ID 和客户端密码的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security - 在Spring Boot中针对LDAP使用Active Directory对用户进行身份验证

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

Spring 3,Spring Security,LDAP,如何向 LDAP 添加角色?

使用服务帐户对 LDAP 目录进行 Spring-Authentication

LDAP - 无法使用spring java ldap api启用用户

如何使用 Spring 安全性从 Active Directory LDAP 填充 LDAP 权限?