最近工作的一个项目中需要调用第三方提供的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是基于spring security的实现的。 spring security可以通过SecurityContextHolder.getContext().getAuthentication()....
spring oauth2 服务端实现例子,eclipse工程(tomcat插件可直接运行)
赠送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 cloud时如果设置了oauth2授权模式,那么应用服务A再调用服务B时使用Feign请求会出现401授权认证的问题,那么解决办法就是在feign调用请求时获取到assessToken并设置到请求header中就可以解决这个...
基于 Spring Cloud Hoxton 、Spring Boot 2.2、 OAuth2 的RBAC权限管理系统 基于数据驱动视图的理念封装 Ant Design Vue,即使没有 vue 的使用经验也能快速上手 提供 lambda 、stream api 、webflux 的生产实践 ...
spring oauth2.0 例子 myeclipse工程
主要介绍了Spring Security OAuth2集成短信验证码登录以及第三方登录,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
#OAuth2-Defender ##主要技术 Maven Spring Boot Spring Security Spring Security OAuth2.0 MySQL ##修改数据库配置 修改defender-oauth2-authorization\src\main\resources\...
spring security oauth2.0 需要的基础 sql 文件
以及spring security oauth2基于数据库的代码配置实现
spring security oauth2的源码,方便研究,备份一下。
spring-boot spring-security-oauth2 完整demo,可以使用微信的方式来获取token和查看资源,注意看代码中的备注
实现功能需要的最小jar集合,其中lib文件夹是导出的jar,maven pom文件夹是maven组织的pom.xml文件。二选一。
从官网下载的oauth2实例sparklr2与tonr2
spring cloud、eureka、gateway、oauth2、dubbo、mybaits、mongo、redis、kafka、mq
验证失效后,Oauth2框架会把异常信息发送到OAuth2AuthenticationEntryPoint类里处理。这时候我们可以在这里做jwt token刷新并跳转。 网上大部分方案也是这种:失效后,使用refresh_token获取新的access_token。并将...
Spring Cloud 安全:集成OAuth2实现身份认证和单点登录 示例代码 Spring Cloud 安全:集成OAuth2实现身份认证和单点登录 示例代码
系统说明:基于 Spring Cloud 2021 、Spring Boot 2.6、 OAuth2 的 RBAC 权限管理系统 基于数据驱动视图的理念封装 element-ui,即使没有 vue 的使用经验也能快速上手 提供对常见容器化支持 Docker、Kubernetes、...
springcloud整合oauth2和jwt实现权限认证,整合mybaits
Spring Cloud OAuth2案例源码