使用 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
方法,我总是只有AnonymousAuthenticationProvider
和DaoAuthenticationProvider
以下可能表明我缺乏经验,但这是一个示例,请阅读可能的疯狂 - 只是想看看我是否可以注入 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密码与传递给matches
的rawPassword
和我从MyClientDetailsService
保持的clientId进行比较有点奇怪/狡猾?
我并不是很熟悉所有的内部结构,但是从 JDBC 实现来看,loadClientByClientId(String clientId)
似乎不需要(或不应该)验证密码。 Spring Security 似乎设置了一个专用的身份验证提供程序,它使用一个 ClientDetailUserDetailsService 将客户端详细信息包装成用户详细信息,并在加载客户端详细信息后比较密码。【参考方案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