Spring 使用'时,需要身份验证才能获得访问令牌;密码';格兰特和斯普林';s ResourceOwnerPasswordResourceDetails

Spring 使用'时,需要身份验证才能获得访问令牌;密码';格兰特和斯普林';s ResourceOwnerPasswordResourceDetails,spring,spring-security,oauth-2.0,spring-security-oauth2,spring-oauth2,Spring,Spring Security,Oauth 2.0,Spring Security Oauth2,Spring Oauth2,我是Spring Security的新手,我想为OAUTH2安全服务实现一个客户端,该服务只接受密码授权 从身份验证服务器获取access\u令牌是使用http正文中的数据完成的,如下所示: client_id={{clientId}}&client_secret={{client_secret}}&grant_type=password&username={{username}}&password={{password}} 之后,必须在标题字段授权中使用访问\

我是Spring Security的新手,我想为OAUTH2安全服务实现一个客户端,该服务只接受
密码
授权

从身份验证服务器获取
access\u令牌
是使用http正文中的数据完成的,如下所示:

client_id={{clientId}}&client_secret={{client_secret}}&grant_type=password&username={{username}}&password={{password}}
之后,必须在标题字段
授权
中使用
访问\u令牌
,以访问实际服务。(例如,
Authorization=bearier对我很有帮助,但我还没有让它发挥作用。
我还发现了一个帖子,其中一个用户遇到了相同的异常,但使用了
client\u凭据
AuthorizationCodeResourceDetails

目前我的代码是这样的

@服务
公共类MyClient{
@自动连线
私有OAuth2RestTemplate restTemplate;
@值(${authServer.accessTokenUri}”)
私有字符串accessTokenUri;
@值(${authServer.clientId}”)
私有字符串clientId;
@值(${authServer.clientSecret}”)
私有字符串clientSecret;
@值(${authServer.username}”)
私有字符串用户名;
@值(${authServer.password}”)
私有字符串密码;
@值(“${serviceUrl}”)
私有字符串serviceUrl;
@豆子
公共OAuth2RestTemplate restTemplate(OAuth2Client上下文OAuth2Client上下文){
OAuth2RestTemplate=新的OAuth2RestTemplate(resource(),oauth2ClientContext);
setAccessTokenProvider(accessTokenProvider());
返回模板;
}
@豆子
公共AccessTokenProvider AccessTokenProvider(){
ResourceOwnerPasswordAccessTokenProvider tokenProvider=新ResourceOwnerPasswordAccessTokenProvider();
返回新的AccessTokenProviderChain(
Arrays.asList(令牌提供者)
);
}
@豆子
受保护的OAuth2ProtectedResourceDetails资源(){
ResourceOwnerPasswordResourceDetails resource=新ResourceOwnerPasswordResourceDetails();
resource.setId(clientId);
setAccessTokenUri(accessTokenUri);
resource.setClientId(clientId);
resource.setClientSecret(clientSecret);
setGrantType(“密码”);
resource.setClientAuthenticationScheme(AuthenticationScheme.form);//通过在HTTP正文中发送身份验证数据获取访问令牌
resource.setAuthenticationScheme(AuthenticationScheme.header);//访问实际服务时,通过HTTP头“Bearer”字段发送访问令牌
resource.setUsername(用户名);
resource.setPassword(密码);
返回资源;
}
public void getDataFromService(){
字符串响应=restTemplate.getForObject(serviceUrl,String.class);
}
}
由于此块,在
AccessTokenProviderChain
中引发异常

if(匿名身份验证令牌的身份验证实例){
如果(!resource.iscliently()){
抛出新的InsufficientAuthenticationException(“获取访问令牌需要身份验证(不允许匿名)”;
}
}
这是异常堆栈跟踪

org.springframework.security.authentication.InsufficientAuthenticationException: Authentication is required to obtain an access token (anonymous not allowed)
    at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken(AccessTokenProviderChain.java:91) ~[spring-security-oauth2-2.3.4.RELEASE.jar:na]
    at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221) ~[spring-security-oauth2-2.3.4.RELEASE.jar:na]
    at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173) ~[spring-security-oauth2-2.3.4.RELEASE.jar:na]
    at org.springframework.security.oauth2.client.OAuth2RestTemplate.createRequest(OAuth2RestTemplate.java:105) ~[spring-security-oauth2-2.3.4.RELEASE.jar:na]
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:731) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    at org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute(OAuth2RestTemplate.java:128) ~[spring-security-oauth2-2.3.4.RELEASE.jar:na]
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:670) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:311) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
如您所见,我无法请求
access\u令牌
。我不理解为什么会出现此异常,因为如果我使用curl命令直接从auth服务器请求
access\u令牌
,我只能使用所提供的数据进行身份验证

在调用
restemplate.getForObject(…)
之前添加以下代码时,我像这样成功地手动获取了
access\u令牌

ResourceOwnerPasswordAccessTokenProvider accessTokenProvider=new ResourceOwnerPasswordAccessTokenProvider();
OAuth2AccessToken token=accessTokenProvider.ActainAccessToken(resource(),new DefaultAccessTokenRequest());
restemplate.getOAuth2ClientContext().setAccessToken(令牌);
String token=restTemplate.getAccessToken();
但是,手动获取
access\u令牌
不是我想要的。我缺少什么吗?是否可以自动获取
access\u令牌
,并使用Spring Security和
密码
授权刷新它? 尽管在Github、StackOverflow等平台上检查了好几个小时的代码,但我还是无法让代码正常工作


更新:

我发现我的
OAuth2RestTemplate
实例中的
ResourceOwnerPasswordResourceDetails
实例没有初始化,当我想在
getDataFromService()
中使用它时(即用户名等字段为空).在@JoeGrandja的澄清和帮助下,我现在的问题不是真正针对Spring安全性,而是针对Spring


如何利用
@Bean
注释方法中的
@Value
注释。目前,当使用
@Bean
注释方法
资源()构建
restTemplate
,来自
应用程序.yml
的值显然还不可用。

在@JoeGrandja的帮助和支持下,我找到了一个解决方案。非常感谢!)

如果其他人有问题,这里是我的工作解决方案。我还建议阅读上面@JoeGrandja的评论

@Configuration
@ConfigurationProperties(prefix = "authserver")
public class AuthServerConfigProperties {

    private String accessTokenUri;
    private String clientId;
    private String grantType;
    private String clientSecret;
    private String username;
    private String password;

   // Getter & Setter for all properties ...
}


@Configuration
public class CommConfig {

    @Autowired
    AuthServerConfigProperties configProperties;

    @Bean
    public OAuth2RestOperations restTemplate(OAuth2ClientContext oauth2ClientContext) {
        OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(resource(), oauth2ClientContext);
        oAuth2RestTemplate.setAccessTokenProvider(new ResourceOwnerPasswordAccessTokenProvider());
        return oAuth2RestTemplate;
    }

    @Bean
    protected OAuth2ProtectedResourceDetails resource() {
        ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
        resource.setId(configProperties.getClientId()); // not necessary
        resource.setAccessTokenUri(configProperties.getAccessTokenUri());
        resource.setClientId(configProperties.getClientId());
        resource.setClientSecret(configProperties.getClientSecret());
        resource.setGrantType(configProperties.getGrantType());
        resource.setClientAuthenticationScheme(AuthenticationScheme.form); // fetch access_token by sending authentication data in HTTP Body
        resource.setAuthenticationScheme(AuthenticationScheme.header); // send access_token via HTTP Header 'Bearer' field when accessing actual service
        resource.setUsername(configProperties.getUsername());
        resource.setPassword(configProperties.getPassword());
        return resource;
    }
}


@RestController
public class MyController {

    @Autowired
    private OAuth2RestOperations restTemplate;

    @Value("${serviceUrl}")
    private String serviceUrl;

    @RequestMapping(value = "/getData", method = RequestMethod.GET)
    @ResponseBody
    public ResponseEntity<String> getData() {
        String response = restTemplate.getForObject(serviceUrl, String.class);
        return new ResponseEntity(response, HttpStatus.OK);
    }
}
@配置
@ConfigurationProperties(前缀=“authserver”)
公共类AuthServerConfigProperties{
私有字符串accessTokenUri;
私有字符串clientId;
私有字符串grantType;
私有字符串clientSecret;
私有字符串用户名;
私有字符串密码;
//所有属性的Getter和Setter。。。
}
@配置
公共类CommConfig{
@自动连线
AuthServerConfigProperties配置属性;
@豆子
公共OAuth2Restorations restTemplate(OAuth2ClientContext OAuth2ClientContext){
OAuth2RestTemplate OAuth2RestTemplate=新的OAuth2RestTemplate(re
public class CustomResourceOwnerPasswordResourceDetails extends ResourceOwnerPasswordResourceDetails {
    @Override
    public boolean isClientOnly() {
        return true;
    }
}