Spring OAuth2 服务器无法使用资源所有者凭据(密码)授权流程刷新令牌
Spring OAuth2 server cannot refresh token with Resource owner credentials (password) grant flow
我已经使用 jwt 令牌配置了具有 spring 安全 oauth 的 OAuth2 授权服务器:
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
...
@Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource).passwordEncoder(passwordEncoder);
}
@Bean
public ApprovalStore approvalStore() {
return new JdbcApprovalStore(dataSource);
}
@Bean
public TokenStore tokenStore() {
var jwtTokenStore = new JwtTokenStore(tokenConverter());
jwtTokenStore.setApprovalStore(approvalStore());
return jwtTokenStore;
}
@Bean
public JwtAccessTokenConverter tokenConverter() {
var converter = new JwtAccessTokenConverter();
var keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource(jwtKeyStore), jwtKeyPass.toCharArray());
converter.setKeyPair(keyStoreKeyFactory.getKeyPair("jwtkey"));
return converter;
}
}
有一个客户获得了 password
和 refresh_token
资助。我可以通过以下请求获取访问和刷新令牌:
curl --request POST \
--url 'http://localhost:8080/oauth/token?grant_type=password&scope=read' \
--header 'authorization: Basic <xxxxxxx>' \
--header 'content-type: application/x-www-form-urlencoded' \
--data 'username=xxxxxxx&password=xxxxxxx'
回复:
{
"access_token": "<long access token>",
"token_type": "bearer",
"refresh_token": "<long refresh token>",
"expires_in": 599,
"scope": "read",
"subject": "xxx",
"jti": "xxx"
}
但是,当我尝试刷新令牌时,出现错误 Invalid refresh token
。进一步调试 Spring 代码,我发现在第一个请求中,它不会在 oauth_approvals
table 中插入一行。在第二次请求(刷新令牌)时,它认为用户尚未批准范围(尽管我有 autoapprove=true
)。
implicit
或 authorization_code
授予流程不是这种情况:在这些情况下,它确实会在 oauth_approvals
table 中插入一行,并且令牌会被刷新成功。
这是 Spring OAuth 中的错误还是有任何解决方法?
在深入研究 Spring 的代码后,我得出结论,这确实是那里的一个错误。所以我扩展了 JdbcApprovalStore
并改用那个。这是伪代码
public class JdbcApprovalStoreAutoApprove extends JdbcApprovalStore {
...
@Override
public List<Approval> getApprovals(String userName, String clientId) {
if (client has auto approved scopes) {
return those scopes;
}
return super.getApprovals(userName, clientId);
}
我已经使用 jwt 令牌配置了具有 spring 安全 oauth 的 OAuth2 授权服务器:
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
...
@Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource).passwordEncoder(passwordEncoder);
}
@Bean
public ApprovalStore approvalStore() {
return new JdbcApprovalStore(dataSource);
}
@Bean
public TokenStore tokenStore() {
var jwtTokenStore = new JwtTokenStore(tokenConverter());
jwtTokenStore.setApprovalStore(approvalStore());
return jwtTokenStore;
}
@Bean
public JwtAccessTokenConverter tokenConverter() {
var converter = new JwtAccessTokenConverter();
var keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource(jwtKeyStore), jwtKeyPass.toCharArray());
converter.setKeyPair(keyStoreKeyFactory.getKeyPair("jwtkey"));
return converter;
}
}
有一个客户获得了 password
和 refresh_token
资助。我可以通过以下请求获取访问和刷新令牌:
curl --request POST \
--url 'http://localhost:8080/oauth/token?grant_type=password&scope=read' \
--header 'authorization: Basic <xxxxxxx>' \
--header 'content-type: application/x-www-form-urlencoded' \
--data 'username=xxxxxxx&password=xxxxxxx'
回复:
{
"access_token": "<long access token>",
"token_type": "bearer",
"refresh_token": "<long refresh token>",
"expires_in": 599,
"scope": "read",
"subject": "xxx",
"jti": "xxx"
}
但是,当我尝试刷新令牌时,出现错误 Invalid refresh token
。进一步调试 Spring 代码,我发现在第一个请求中,它不会在 oauth_approvals
table 中插入一行。在第二次请求(刷新令牌)时,它认为用户尚未批准范围(尽管我有 autoapprove=true
)。
implicit
或 authorization_code
授予流程不是这种情况:在这些情况下,它确实会在 oauth_approvals
table 中插入一行,并且令牌会被刷新成功。
这是 Spring OAuth 中的错误还是有任何解决方法?
在深入研究 Spring 的代码后,我得出结论,这确实是那里的一个错误。所以我扩展了 JdbcApprovalStore
并改用那个。这是伪代码
public class JdbcApprovalStoreAutoApprove extends JdbcApprovalStore {
...
@Override
public List<Approval> getApprovals(String userName, String clientId) {
if (client has auto approved scopes) {
return those scopes;
}
return super.getApprovals(userName, clientId);
}