Spring Security OAuth - 未为空资源配置提供程序管理器

Posted

技术标签:

【中文标题】Spring Security OAuth - 未为空资源配置提供程序管理器【英文标题】:Spring Security OAuth - Provider Manager is Not Configured for Null Resource 【发布时间】:2017-12-11 05:09:36 【问题描述】:

我正在尝试使用 Spring Secruity 的 OAuth API 从外部发布的 API 获取访问令牌。

这个 curl 命令有效(它的内容就是我获取访问令牌所需的全部内容):

curl -X POST \
https://api.app.com/v1/oauth/token \
  -H 'content-type: application/x-www-form-urlencoded' \
  -d'grant_type=client_credentials&client_id=bcfrtew123&client_secret=Y67493012'

运行此 curl 命令后,我能够从外部服务获取访问令牌。

使用 Spring Security OAuth API 时:

<dependency>
   <groupId>org.springframework.security.oauth</groupId>
     <artifactId>spring-security-oauth2</artifactId>
     <version>2.1.1.RELEASE</version>
</dependency>

像这样设置我的 SpringMVC 控制器的方法:

@RequestMapping(value = "/getAccessToken", method = RequestMethod.POST, consumes="application/x-www-form-urlencoded")
public OAuth2AccessToken getAccessToken(@RequestParam(value="client_id", required=true) String clientId, @RequestParam(value="client_secret", required=true) String clientSecret) throws Exception 
    String tokenUri = "https://api.app.com/v1/oauth/token";

    ResourceOwnerPasswordResourceDetails resourceDetails = new ResourceOwnerPasswordResourceDetails();

    resourceDetails.setAccessTokenUri(tokenUri);
    resourceDetails.setClientId(clientId);
    resourceDetails.setClientSecret(clientSecret);
    resourceDetails.setGrantType("client_credentials");
    resourceDetails.setScope(Arrays.asList("read", "write"));

    DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext();

    oauth2RestTemplate = new OAuth2RestTemplate(resourceDetails, clientContext);

    OAuth2AccessToken token = oauth2RestTemplate.getAccessToken();
    return token;

当我从本地 tomcat 实例调用 getAccessToken 时:

access_denied 
error_description=Unable to obtain a new access token for resource 'null'. 
The provider manager is not configured to support it.

问题:

    我在这里缺少什么?这需要一些注释吗?是否存在未设置或需要的属性?

(请注意,内容类型需要是“application/x-www-form-urlencoded”...)

    如何使用 Spring Security OAuth API 模拟有效的 curl 命令?

    会不会是我在 RequestParameters 中设置的默认值?

    如果成功,我该如何设置,以便在发出任何请求之前始终预加载访问令牌?

【问题讨论】:

【参考方案1】:

真正的原因是,您正在使用 ResourceOwnerPasswordResourceDetails 进行 "client_credentials" 访问令牌请求。我们不能互换ResourceOwnerPasswordResourceDetailsClientCredentialsResourceDetails

ClientCredentialsResourceDetails 中,您需要设置AccessTokenUri, ClientId, ClientSecretgrantType。 在ResourceOwnerPasswordResourceDetails 中,您需要提供Username and Password 以及AccessTokenUri, ClientId, ClientSecretGrantType

(一些身份验证服务器确实接受password 令牌请求而没有username and password.. 但我会说这是错误的)

参考:Securing an existing API with our own solution

【讨论】:

【参考方案2】:

这是我用的:

@Bean
public RestTemplate oAuthRestTemplate() 
    ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails();
    resourceDetails.setId("1");
    resourceDetails.setClientId(oAuth2ClientId);
    resourceDetails.setClientSecret(oAuth2ClientSecret);
    resourceDetails.setAccessTokenUri(accessTokenUri);

    OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, oauth2ClientContext);

    return restTemplate;

框架将设置正确的标头,但用户名/密码将被 base64 编码为授权标头(基本身份验证)。这是 client_credentials 授权的 OAuth2 规范。

检查api是否支持规范:

curl -X POST \
'https://api.app.com/v1/oauth/token' \
-i -u 'client:secret' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=client_credentials'

如果您需要将用户名和密码作为数据而不是授权头发送,您可以添加resourceDetails.setAuthenticationScheme(AuthenticationScheme.form); 这应该将用户名和密码设置为数据

【讨论】:

对我来说是setId("1")。我一设置它就开始工作了。 setId("1") 的意义是什么? 来自文档:Get a unique identifier for these protected resource details.【参考方案3】:

这可能是因为服务器无法识别您发布到该特定 URL 的内容类型。 在您的 CURL 请求中,尝试使用 http 标头为自定义控制器包含“content-type: application/x-www-form-urlencoded”。

另外,您还没有为 resourceDetails 设置用户名和密码。 resourceDetails.setUserName("用户"); resourceDetails.setUserName("密码");

如果这些不起作用,请尝试提取使用 application/x-www-form-urlencoded 编码的请求并通过 RestTemplate 将其作为字符串传递,您可以获得令牌。

如果您需要更多支持,请告诉我。

试试下面给出令牌和字符串响应的代码。

【讨论】:

感谢您的回复!既然这是 OAuth2RestTemplate 而不是 RestTemplate,我该如何设置?另外,如果您查看 curl,则没有设置用户名/密码。 那么您剩下的选择是将 content_type 包含为 application/x-www-form-urlencoded 问题是我使用的是 RestTemplate 并且您正在使用 oauth2RestTemplate 并且您无法在其中设置任何 Http 标头,除非您将其更改为 RestTempalate。如果你能找到一种方法来改变我可以提供代码。 JavaDoc 说 OAuth2RestTemplate 是 RestTemplate 的子类:docs.spring.io/spring-security/oauth/apidocs/org/… 我在答案中添加了另一个新代码,请查看。

以上是关于Spring Security OAuth - 未为空资源配置提供程序管理器的主要内容,如果未能解决你的问题,请参考以下文章

java + spring security oauth token 未返回,找不到 404 页面

Spring Security oauth2Login OAuth2UserService 认证后未执行

Spring Security Oauth2:主体对象未返回扩展的用户详细信息

Spring Boot Security - 如果用户未通过 Oauth2 进行身份验证,如何禁用对 swagger ui 页面的端点的访问

Spring Security 4 + OAuth2 = 坏凭证

Spring Security Oauth2 : Possible CSRF detected