无法使用编码密码向 spring oauth 授权服务器进行身份验证
Cannot authenticate to spring oauth authorization server with an encoded password
我正在学习如何将 oauth2 集成到我们的 spring 引导项目中以保护我们的 API。因此,我从一个从 spring-security-oauth2-boot 中读取的运行良好的基本示例开始,然后当我尝试修改密码编码器以使用我无法验证的任何编码器时。
这是我的授权服务器配置:
@Component
@EnableResourceServer
@EnableAuthorizationServer
class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
AuthenticationManager authenticationManager
AuthorizationServerConfiguration(AuthenticationConfiguration authenticationConfiguration) throws Exception {
this.authenticationManager = authenticationConfiguration.getAuthenticationManager()
}
@Override
void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("myclient")
//.secret("{noop}password") <-- this works
.secret("{pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc")
.authorizedGrantTypes("client_credentials")
.scopes("all")
.accessTokenValiditySeconds(3600)
}
@Override
void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
}
}
当我使用 {noop}password
时,我可以通过 curl 成功进行身份验证:
$ curl myclient:password@localhost:8080/oauth/token?scope=all -d grant_type=client_credentials
{"access_token":"4320fa79-38c2-45b1-a788-5ea1b5ce881a","token_type":"bearer","expires_in":3599,"scope":"all"}
但是当我打开任何散列时,我无法进行身份验证。我试过这种方式来使用 curl 进行身份验证,但没有成功:
$ curl congero:5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc@localhost:8080/oauth/token?scope=all -d grant_type=client_credentials
{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}
$ curl congero:{pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc@localhost:8080/oauth/token?scope=all -d grant_type=client_credentials
{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}
我还在代码中添加了这个代码片段来生成密码以进行双重检查,这给了我一个不同的散列,也用 curl 进行了测试,但也没有运气:
PasswordEncoder passwordEncoder = new Pbkdf2PasswordEncoder()
String encoded = passwordEncoder.encode('password')
println "PASSWORD: $encoded"
$ curl congero:91d1ee4784686a2e2a39c214f5a4b3ebb41e1206e2d1fc770d3ff146b034f8c156ea279c73aa1629@localhost:8080/oauth/token?scope=all -d grant_type=client_credentials
{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}
$ curl congero:{pbkdf2}91d1ee4784686a2e2a39c214f5a4b3ebb41e1206e2d1fc770d3ff146b034f8c156ea279c73aa1629@localhost:8080/oauth/token?scope=all -d grant_type=client_credentials
{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}
我调试了 spring 源代码,发现在密码编码器(Pbkdf2PasswordEncoder 和 BcryptPasswordEncoder)中,密码是用这种方法解码的:
private byte[] decode(String encodedBytes) {
if(this.encodeHashAsBase64) {
return Base64.getDecoder().decode(encodedBytes);
}
return Hex.decode(encodedBytes);
}
这 Hex.decode
看起来是密码不匹配的罪魁祸首,使用 {noop}
时不会发生这种情况。
知道我做错了什么吗?我错过了任何重要的步骤吗?该文档对我来说不清楚,因为它没有显示如何逐步自定义配置。
当你打开散列时,并不意味着你需要在你的curl命令中传递编码后的密码,只需传递“password
”,spring将对其进行编码并与它进行比较
"{pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc"
您需要一个 Pbkdf2PasswordEncoder
类型的 bean,这样 spring 将在散列和比较期间使用
您可以使用 DelegatingPasswordEncoder
即
A password encoder that delegates to another PasswordEncoder based
upon a prefixed identifier.
Password Storage Format
The general format for a password is: {id}encodedPassword
Such that "id" is an identifier used to look up which PasswordEncoder
should be used and "encodedPassword" is the original encoded password
for the selected PasswordEncoder. The "id" must be at the beginning of
the password, start with "{" and end with "}". If the "id" cannot be
found, the "id" will be null. For example, the following might be a
list of passwords encoded using different "id". All of the original
passwords are "password".
{bcrypt}a$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
{noop}password
{pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc
对于我们上面构造的DelegatingPasswordEncoder:
- 第一个密码的 PasswordEncoder id 为 "bcrypt",encodedPassword 为
“$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG”。什么时候
匹配它会委托给
BCryptPasswordEncoder
.
- 第二个将委托给
NoOpPasswordEncoder
。
- 第三个密码将委托给
Pbkdf2PasswordEncoder
。
Password Matching is done based upon the "id" and the mapping
of the "id" to the PasswordEncoder provided in the constructor.
因此,要使其正常工作,您应该声明一个 PasswordEncoder
bean(基本上您可以毫无问题地在密码编码器之间切换,每个新创建的用户都可以免费迁移!):
@Bean
PasswordEncoder passwordEncoder(){
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
我正在学习如何将 oauth2 集成到我们的 spring 引导项目中以保护我们的 API。因此,我从一个从 spring-security-oauth2-boot 中读取的运行良好的基本示例开始,然后当我尝试修改密码编码器以使用我无法验证的任何编码器时。
这是我的授权服务器配置:
@Component
@EnableResourceServer
@EnableAuthorizationServer
class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
AuthenticationManager authenticationManager
AuthorizationServerConfiguration(AuthenticationConfiguration authenticationConfiguration) throws Exception {
this.authenticationManager = authenticationConfiguration.getAuthenticationManager()
}
@Override
void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("myclient")
//.secret("{noop}password") <-- this works
.secret("{pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc")
.authorizedGrantTypes("client_credentials")
.scopes("all")
.accessTokenValiditySeconds(3600)
}
@Override
void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
}
}
当我使用 {noop}password
时,我可以通过 curl 成功进行身份验证:
$ curl myclient:password@localhost:8080/oauth/token?scope=all -d grant_type=client_credentials
{"access_token":"4320fa79-38c2-45b1-a788-5ea1b5ce881a","token_type":"bearer","expires_in":3599,"scope":"all"}
但是当我打开任何散列时,我无法进行身份验证。我试过这种方式来使用 curl 进行身份验证,但没有成功:
$ curl congero:5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc@localhost:8080/oauth/token?scope=all -d grant_type=client_credentials
{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}
$ curl congero:{pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc@localhost:8080/oauth/token?scope=all -d grant_type=client_credentials
{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}
我还在代码中添加了这个代码片段来生成密码以进行双重检查,这给了我一个不同的散列,也用 curl 进行了测试,但也没有运气:
PasswordEncoder passwordEncoder = new Pbkdf2PasswordEncoder()
String encoded = passwordEncoder.encode('password')
println "PASSWORD: $encoded"
$ curl congero:91d1ee4784686a2e2a39c214f5a4b3ebb41e1206e2d1fc770d3ff146b034f8c156ea279c73aa1629@localhost:8080/oauth/token?scope=all -d grant_type=client_credentials
{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}
$ curl congero:{pbkdf2}91d1ee4784686a2e2a39c214f5a4b3ebb41e1206e2d1fc770d3ff146b034f8c156ea279c73aa1629@localhost:8080/oauth/token?scope=all -d grant_type=client_credentials
{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}
我调试了 spring 源代码,发现在密码编码器(Pbkdf2PasswordEncoder 和 BcryptPasswordEncoder)中,密码是用这种方法解码的:
private byte[] decode(String encodedBytes) {
if(this.encodeHashAsBase64) {
return Base64.getDecoder().decode(encodedBytes);
}
return Hex.decode(encodedBytes);
}
这 Hex.decode
看起来是密码不匹配的罪魁祸首,使用 {noop}
时不会发生这种情况。
知道我做错了什么吗?我错过了任何重要的步骤吗?该文档对我来说不清楚,因为它没有显示如何逐步自定义配置。
当你打开散列时,并不意味着你需要在你的curl命令中传递编码后的密码,只需传递“password
”,spring将对其进行编码并与它进行比较
"{pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc"
您需要一个 Pbkdf2PasswordEncoder
类型的 bean,这样 spring 将在散列和比较期间使用
您可以使用 DelegatingPasswordEncoder
即
A password encoder that delegates to another PasswordEncoder based upon a prefixed identifier.
Password Storage Format
The general format for a password is:
{id}encodedPassword
Such that "id" is an identifier used to look up which PasswordEncoder should be used and "encodedPassword" is the original encoded password for the selected PasswordEncoder. The "id" must be at the beginning of the password, start with "{" and end with "}". If the "id" cannot be found, the "id" will be null. For example, the following might be a list of passwords encoded using different "id". All of the original passwords are "password".
{bcrypt}a$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
{noop}password {pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc
对于我们上面构造的DelegatingPasswordEncoder:
- 第一个密码的 PasswordEncoder id 为 "bcrypt",encodedPassword 为
“$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG”。什么时候
匹配它会委托给
BCryptPasswordEncoder
. - 第二个将委托给
NoOpPasswordEncoder
。 - 第三个密码将委托给
Pbkdf2PasswordEncoder
。
Password Matching is done based upon the "id" and the mapping of the "id" to the PasswordEncoder provided in the constructor.
因此,要使其正常工作,您应该声明一个 PasswordEncoder
bean(基本上您可以毫无问题地在密码编码器之间切换,每个新创建的用户都可以免费迁移!):
@Bean
PasswordEncoder passwordEncoder(){
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}