`

记录使用Spring OAuth2RestTemplate 遇到的坑

阅读更多
   最近工作的一个项目中需要调用第三方提供的API获取数据,该API接口采用的授权方式是OAuth2,授权类型采用client_crdentials。鉴于Spring框架完善的生态系统我们直接采用了spring-security-oauth2 框架中提供的OAuth2客户端能力。现将使用框架过程中遇到的几个问题记录,供以后参考,以希望其他开发的同学能够避免踩坑。

(1)关于security.oauth2.client.*几个相关配置项容易混淆
  •     .security.oauth2.client.authentication-scheme
  •        这个配置项主要指定访问API时传输bearer token的方式,有四个可选值
           header,query,form,none,
           默认值为:header,表示在HTTP头中传输
  •     .security.oauth2.client.client-authentication-scheme
  •        这个配置项主要指定进行客户端认证时传输client_id,client-secret的方式,同样有四
           个可选值header,query,form,none,
           默认值为:header,表示在HTTP头中传输
  •     .security.oauth2.client.grant-type
  •         这个配置项主要指定进行客户端获取access_token的授予类型,有四个可选值
            authorization_code,client_credentials,password,implicit
            Spring OAuth2框架默认会采用authorization_code (授权码授予)access_token方式
            这里一定要记得根据API的access_token授予类型配置,例如我们访问的API实现的是
            client_crdentials 这种授予类型因此要配置成:
            security.oauth2.client.grant-type=client_crdentials

(2)  关于配置 OAuth2RestTemplate BEAN实例化时要传入正确的OAuth2资源保护类型
	public OAuth2RestTemplate(OAuth2ProtectedResourceDetails resource, OAuth2ClientContext context) {
		super();
		if (resource == null) { //不能传空的
			throw new IllegalArgumentException("An OAuth2 resource must be supplied.");
		}

		this.resource = resource;
		this.context = context;
		[color=red]setErrorHandler(new OAuth2ErrorHandler(resource))[/color];//设置出错处理器
	}

       Spring OAuth2框架默认会使用AuthorizationCodeResourceDetails来映射配置项,例如果想要使用client_crdentials,就必须采用ClientCredentialsResourceDetails,这个类必须要自己实例化,否则框架默认会传AuthorizationCodeResourceDetails给你,这个地方要特别注意。
         
   @Bean
    public OAuth2RestTemplate oauth2RestTemplate(OAuth2ClientContext oauth2ClientContext,
            OAuth2ProtectedResourceDetails details) {
        ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails();//根据资源的访问授预类型来选取
         ....
        OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, oauth2ClientContext);
        ....
    }

      探究一下原码OAuth2RestTemplate容易发现,它内部委托AccessTokenProvider 获取access_token,内部管理着一AccessTokenProviderChain,如下所示:
private AccessTokenProvider accessTokenProvider = new AccessTokenProviderChain(Arrays.<AccessTokenProvider> asList(
			new AuthorizationCodeAccessTokenProvider(), new ImplicitAccessTokenProvider(),
			new ResourceOwnerPasswordAccessTokenProvider(), new ClientCredentialsAccessTokenProvider()));

在acquireAccessToken方法中委托
protected OAuth2AccessToken acquireAccessToken(OAuth2ClientContext oauth2Context){
   ....
   accessToken = accessTokenProvider.obtainAccessToken(resource, accessTokenRequest);
   ....
}

AccessTokenProviderChain 使用 List<AccessTokenProvider> chain 集合中的每一种AccessTokenProvider尝试根据OAuth2ProtectedResourceDetails的类型调用如下具体AccessTokenProvider的实现类完成access_token的获取,同样有4个实现类:1.ClientCredentialsAccessTokenProvider
2.AuthorizationCodeAccessTokenProvider
3.ImplicitAccessTokenProvider
4.ResourceOwnerPasswordAccessTokenProvider

每个实现都有一个supportsResource 方法检查配置的OAuth2资源保护类型自己是否能处理,以ClientCredentialsAccessTokenProvider为例
	public boolean supportsResource(OAuth2ProtectedResourceDetails resource) {
		return resource instanceof ClientCredentialsResourceDetails
				&& "client_credentials".equals(resource.getGrantType());
	}

(3)OAuth2RestTemplate 默认对错误的处理可能导致API返回的业务错误信息我们无法获取
     在前面的代码片段里我们知道OAuth2RestTemplate  把出错处理交给了类OAuth2ErrorHandler处理,这个类有一个重要的方法来判断响应状态码是否是4XX,5XX:
public boolean hasError(ClientHttpResponse response) throws IOException {
		return HttpStatus.Series.CLIENT_ERROR.equals(response.getStatusCode().series())
				|| this.errorHandler.hasError(response);
	}

然后OAuth2ErrorHandler又交给了默认的 DefaultResponseErrorHandler 来处理,该个类会将5xx的错误包装成HttpServerErrorException 走了WEB的出错理机制。因此通过ResponseEntity无法获业务错误信息。
      此时就需要定制OAuth2ErrorHandler,主要是重写hasError方法,如下所示:
     public class NoOpResponseErrorHandler extends OAuth2ErrorHandler {

    public NoOpResponseErrorHandler(OAuth2ProtectedResourceDetails resource) {
        super(resource);
    }
    
    @Override
    public boolean hasError(ClientHttpResponse response) throws IOException {
        return response.getStatusCode().equals(HttpStatus.UNAUTHORIZED) ||
                response.getStatusCode().equals(HttpStatus.FORBIDDEN);
    }

}

然后通过调用OAuth2RestTemplate 的 setErrorHandler()方法注入
(4)关于Error creating bean with name 'scopedTarget.oauth2ClientContext' despite defining RequestContextListener 异常
    通过在配置类中添加
@Bean
@Order(0)
public RequestContextListener requestContextListener() {
    return new RequestContextListener();
}

或在
web.xml文件中添加片段
<listener>
 <listener-class>
        org.springframework.web.context.request.RequestContextListener
 </listener-class>
</listener>

总之想要快速处理问题就需要我们对其框架原理有基本的理解和研究,这样才能少走弯路
最后推荐几个链接,大家可以去了解一下
(1)Spring Boot and OAuth2
https://spring.io/guides/tutorials/spring-boot-oauth2/
(2)Protecting REST API with OAuth2
http://stackmirror.caup.cn/page/s18eicq1kmrc
分享到:
评论

相关推荐

    spring oauth2 服务端

    **Spring OAuth2 服务端详解** OAuth2 是一个授权框架,它允许第三方应用在用户授权的情况下,访问其存储在另一应用中的特定资源。Spring OAuth2 是 Spring Security 的扩展,为 Java 开发者提供了实现 OAuth2 的...

    spring-security-oauth2源码

    - 示例:展示如何配置和使用Spring Security OAuth2的示例项目。 - 测试:单元测试和集成测试,用于验证框架的正确性。 - 文档:关于框架使用的文档和API参考。 理解Spring Security OAuth2的关键在于熟悉OAuth2...

    spring security + spring oauth2 +spring mvc SSO单点登录需要的最小jar包集合

    1. **身份验证中心(Authentication Server)**:使用Spring Security和Spring OAuth2建立一个独立的授权服务器,处理用户的登录请求,验证用户凭证,并生成访问令牌(Access Token)。 2. **资源服务器(Resource ...

    在SpringCloud 使用Security+Oauth2 时候再去使用fegin,发现出现401未授权

    我们在使用spring cloud时如果设置了oauth2授权模式,那么应用服务A再调用服务B时使用Feign请求会出现401授权认证的问题,那么解决办法就是在feign调用请求时获取到assessToken并设置到请求header中就可以解决这个...

    Spring Security OAuth2集成短信验证码登录以及第三方登录

    Spring Security OAuth2是基于Spring Cloud/Spring Boot环境下使用OAuth2.0的解决方案,为开发者提供了全套的组件来支持OAuth2.0认证。然而,在开发过程中,我们会发现由于Spring Security OAuth2的组件特别全面,...

    spring oauth2 认证,jar包, demo程序

    Spring OAuth2 是一个广泛使用的授权框架,用于构建安全的、可扩展的应用程序。它基于OAuth2协议,允许第三方应用在用户授权的情况下访问受保护的资源。在这个“spring oauth2 认证”项目中,我们有一个名为`spring-...

    spring oauth2获取当前登录用户信息.docx

    通常,Spring Security允许我们通过`SecurityContextHolder.getContext().getAuthentication().getPrincipal()`来获取详细的用户信息,但当你尝试在OAuth2环境中使用同样的方法时,你可能只会得到当前用户的用户名,...

    spring-security-oauth2-2.3.5.RELEASE-API文档-中文版.zip

    赠送jar包:spring-security-oauth2-2.3.5.RELEASE.jar; 赠送原API文档:spring-security-oauth2-2.3.5.RELEASE-javadoc.jar; 赠送源代码:spring-security-oauth2-2.3.5.RELEASE-sources.jar; 赠送Maven依赖信息...

    spring-boot spring-security-oauth2 完整demo

    在这个完整的demo中,开发者已经构建了一个使用Spring Boot、Spring Security和OAuth2的系统。关键在于如何配置OAuth2以实现微信式授权。微信授权流程通常包括以下步骤: 1. 用户访问应用,应用重定向至微信授权...

    Spring cloud Oauth2的密码模式数据库方式实现登录授权验证

    Spring Cloud OAuth2是一种基于OAuth2协议的授权框架,它为微服务架构提供了安全的认证和授权服务。在“Spring Cloud Oauth2的密码模式数据库方式实现登录授权验证”这个主题中,我们将深入探讨如何利用OAuth2的密码...

    Spring security oauth源码

    3. **OAuth2 endpoints**:如`/oauth/authorize`、`/oauth/token`等,用于处理OAuth相关的请求,如用户授权、令牌颁发等。 4. **Resource Server配置**:用于配置资源服务器如何验证访问令牌,通常使用`@...

    基于Spring Boot+Spring Cloud+OAuth2的RBAC权限管理系统.zip

    基于 Spring Cloud Hoxton 、Spring Boot 2.2、 OAuth2 的RBAC权限管理系统 基于数据驱动视图的理念封装 Ant Design Vue,即使没有 vue 的使用经验也能快速上手 提供 lambda 、stream api 、webflux 的生产实践 ...

    spring oauth2.0 例子

    1. **配置OAuth2.0服务器**:在Spring中,我们通常使用`Spring Security OAuth2`库来设置授权服务器。你需要配置`AuthorizationServerConfigurerAdapter`以定义客户端详细信息、令牌端点、刷新令牌策略等。 2. **...

    Spring Security OAuth2认证授权示例详解

    在这个示例中,我们将深入探讨如何使用Spring Security OAuth2实现OAuth2授权流程。 1. **OAuth2的角色**: - **资源所有者**:通常是应用程序的用户,他们拥有数据,决定是否允许第三方应用访问。 - **客户端**...

    spring oauth 配置实现

    Spring Security OAuth2 是一个强大的框架,用于为应用程序提供授权(Authorization)和认证(Authentication)功能。这个框架基于OAuth2协议,允许第三方应用在用户授权的情况下访问受保护的资源。在这个配置实现中...

    Spring Cloud 集成OAuth2实现身份认证和单点登录

    压缩包中的`spring-cloud-examples-master`文件可能包含了示例代码,这些代码展示了如何在Spring Cloud项目中配置和使用OAuth2和SSO。通过学习这些示例,你可以了解到如何定义OAuth2的客户端、如何配置授权服务器和...

    spring security oauth2

    Spring Security OAuth2 是一个强大的框架,用于为基于Spring的应用程序提供认证和授权功能。这个框架是Spring Security的扩展,专门设计来处理OAuth2协议,它允许开发者安全地开放应用程序的API,同时保护用户数据...

    springcloud整合oauth2和jwt

    在Spring Cloud中,我们可以使用Spring Security OAuth2模块来实现OAuth2的授权服务器和资源服务器。 1. **OAuth2流程**: - 授权请求:客户端(如浏览器或移动应用)引导用户到授权服务器进行登录。 - 用户授权...

    spring cloud oauth2

    Spring Cloud OAuth2 是一个基于 Spring Boot 和 Spring Security 的 OAuth2 实现,用于为微服务架构提供安全认证和授权服务。OAuth2 是一个开放标准,它允许用户授权第三方应用访问其私有资源,而无需分享用户名和...

Global site tag (gtag.js) - Google Analytics