Spring Cloud Feign 使用OAuth2
Spring Cloud 微服务架构下,服务间的调用采用的是Feign组件,为了增加服务安全性,server之间互相调用采用OAuth2的client模式。Feign使用http进行服务间的通信,同时整合了Ribbion
使得其具有负载均衡和失败重试的功能,微服务service-a调用service-b的流程 中大概流程 :
Feign调用间采用OAuth2验证的配置:
(1)采用SpringBoot自动加载机制 定义注解继承@EnableOAuth2Client
@Import({OAuth2FeignConfigure.class})
@EnableOAuth2Client
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface EnableFeignOAuth2Client { }
(2)定义配置类OAuth2FeignConfigure
public class OAuth2FeignConfigure {
// feign的OAuth2ClientContext
private OAuth2ClientContext feignOAuth2ClientContext = new DefaultOAuth2ClientContext(); @Resource
private ClientCredentialsResourceDetails clientCredentialsResourceDetails; @Autowired
private ObjectFactory<HttpMessageConverters> messageConverters; @Bean
public OAuth2RestTemplate clientCredentialsRestTemplate(){
return new OAuth2RestTemplate(clientCredentialsResourceDetails);
} @Bean
public RequestInterceptor oauth2FeignRequestInterceptor(){
return new OAuth2FeignRequestInterceptor(feignOAuth2ClientContext, clientCredentialsResourceDetails);
} @Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
} @Bean
public Retryer retry() {
// default Retryer will retry 5 times waiting waiting
// 100 ms per retry with a 1.5* back off multiplier
return new Retryer.Default(100, SECONDS.toMillis(1), 3);
} @Bean
public Decoder feignDecoder() {
return new CustomResponseEntityDecoder(new SpringDecoder(this.messageConverters), feignOAuth2ClientContext);
} /**
* Http响应成功 但是token失效,需要定制 ResponseEntityDecoder
* @author maxianming
* @date 2018/10/30 9:47
*/
class CustomResponseEntityDecoder implements Decoder {
private org.slf4j.Logger log = LoggerFactory.getLogger(CustomResponseEntityDecoder.class); private Decoder decoder; private OAuth2ClientContext context; public CustomResponseEntityDecoder(Decoder decoder, OAuth2ClientContext context) {
this.decoder = decoder;
this.context = context;
} @Override
public Object decode(final Response response, Type type) throws IOException, FeignException {
if (log.isDebugEnabled()) {
log.debug("feign decode type:{},reponse:{}", type, response.body());
}
if (isParameterizeHttpEntity(type)) {
type = ((ParameterizedType) type).getActualTypeArguments()[0];
Object decodedObject = decoder.decode(response, type);
return createResponse(decodedObject, response);
}
else if (isHttpEntity(type)) {
return createResponse(null, response);
}
else {
// custom ResponseEntityDecoder if token is valid then go to errorDecoder
String body = Util.toString(response.body().asReader());
if (body.contains(ServerConstant.INVALID_TOKEN.getCode())) {
clearTokenAndRetry(response, body);
}
return decoder.decode(response, type);
}
} /**
* token失效 则将token设置为null 然后重试
* @author maxianming
* @param
* @return
* @date 2018/10/30 10:05
*/
private void clearTokenAndRetry(Response response, String body) throws FeignException {
log.error("接收到Feign请求资源响应,响应内容:{}",body);
context.setAccessToken(null);
throw new RetryableException("access_token过期,即将进行重试", new Date());
} private boolean isParameterizeHttpEntity(Type type) {
if (type instanceof ParameterizedType) {
return isHttpEntity(((ParameterizedType) type).getRawType());
}
return false;
} private boolean isHttpEntity(Type type) {
if (type instanceof Class) {
Class c = (Class) type;
return HttpEntity.class.isAssignableFrom(c);
}
return false;
} @SuppressWarnings("unchecked")
private <T> ResponseEntity<T> createResponse(Object instance, Response response) { MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
for (String key : response.headers().keySet()) {
headers.put(key, new LinkedList<>(response.headers().get(key)));
}
return new ResponseEntity<>((T) instance, headers, org.springframework.http.HttpStatus.valueOf(response
.status()));
}
} @Bean
public ErrorDecoder errorDecoder() {
return new RestClientErrorDecoder(feignOAuth2ClientContext);
} /**
* Feign调用HTTP返回响应码错误时候,定制错误的解码
* @author maxianming
* @date 2018/10/30 9:45
*/
class RestClientErrorDecoder implements ErrorDecoder {
private org.slf4j.Logger logger = LoggerFactory.getLogger(RestClientErrorDecoder.class); private OAuth2ClientContext context; RestClientErrorDecoder(OAuth2ClientContext context) {
this.context = context;
} public Exception decode(String methodKey, Response response) {
logger.error("Feign调用异常,异常methodKey:{}, token:{}, response:{}", methodKey, context.getAccessToken(), response.body());
if (HttpStatus.SC_UNAUTHORIZED == response.status()) {
logger.error("接收到Feign请求资源响应401,access_token已经过期,重置access_token为null待重新获取。");
context.setAccessToken(null);
return new RetryableException("疑似access_token过期,即将进行重试", new Date());
}
return errorStatus(methodKey, response);
}
} }
1、使用ClientCredentialsResourceDetails (即client_id、 client-secret、user-info-uri等信息配置在配置中心)初始化OAuth2RestTemplate,用户请求创建token时候验证基本信息
2、主要定义了拦截器初始化了OAuth2FeignRequestInterceptor ,使得Feign进行RestTemplate调用的请求前进行token拦截。 如果不存在token则需要auth-server中获取token
3、注意上下文对象OAuth2ClientContext建立后不放在Bean容器中,主要放在Bean容器,Spring mvc的前置处理器, 会复制token到OAuth2ClientContext中, 导致用户的token会覆盖服务间的token当不同 token间的权限不同时,验证会不通过。
4、重新定义了 Decoder 即,RestTemple http调用的响应进行解码, 由于token失效时进行了扩展,
默认情况下:token失效会返回401错误的http响应,导致进入ErrorDecoder流程,在ErrorDecoder中如果token过期,则进行除掉token,Feign重试。
扩展后:返回的是token失效的错误码,所以会走Decoder流程,所以对ResponseEntityDecoder进行了扩展,如果无效token错误码,则清空token并重试。
Spring Cloud Feign 使用OAuth2的更多相关文章
- Spring Cloud下基于OAUTH2+ZUUL认证授权的实现
Spring Cloud下基于OAUTH2认证授权的实现 在Spring Cloud需要使用OAUTH2来实现多个微服务的统一认证授权,通过向OAUTH服务发送某个类型的grant type进行集中认 ...
- 笔记:Spring Cloud Feign Ribbon 配置
由于 Spring Cloud Feign 的客户端负载均衡是通过 Spring Cloud Ribbon 实现的,所以我们可以直接通过配置 Ribbon 的客户端的方式来自定义各个服务客户端调用的参 ...
- 笔记:Spring Cloud Feign Hystrix 配置
在 Spring Cloud Feign 中,除了引入了用户客户端负载均衡的 Spring Cloud Ribbon 之外,还引入了服务保护与容错的工具 Hystrix,默认情况下,Spring Cl ...
- 笔记:Spring Cloud Feign 其他配置
请求压缩 Spring Cloud Feign 支持对请求与响应进行GZIP压缩,以减少通信过程中的性能损耗,我们只需要通过下面二个参数设置,就能开启请求与响应的压缩功能,yml配置格式如下: fei ...
- 笔记:Spring Cloud Feign 声明式服务调用
在实际开发中,对于服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以我们通常会针对各个微服务自行封装一些客户端类来包装这些依赖服务的调用,Spring Cloud Feign 在此基础上做了进 ...
- Spring Cloud下基于OAUTH2认证授权的实现
GitHub(spring -boot 2.0.0):https://github.com/bigben0123/uaa-zuul 示例(spring -boot 2.0.0): https://gi ...
- 第六章:声明式服务调用:Spring Cloud Feign
Spring Cloud Feign 是基于 Netflix Feign 实现的,整合了 Spring Cloud Ribbon 和 Spring Cloud Hystrix,除了提供这两者的强大功能 ...
- Spring Cloud Feign Ribbon 配置
由于 Spring Cloud Feign 的客户端负载均衡是通过 Spring Cloud Ribbon 实现的,所以我们可以直接通过配置 Ribbon 的客户端的方式来自定义各个服务客户端调用的参 ...
- Spring Cloud feign
Spring Cloud feign使用 前言 环境准备 应用模块 应用程序 应用启动 feign特性 综上 1. 前言 我们在前一篇文章中讲了一些我使用过的一些http的框架 服务间通信之Http框 ...
随机推荐
- 常用的SQL
--时间计算: select GETDATE() ,GETDATE()) ,GETDATE()) ,GETDATE()) ,GETDATE()) ,GETDATE()) ,GETDATE()) --查 ...
- php 不等待返回的实现方法(异步调用)
PHP异步执行的常用方式常见的有以下几种,可以根据各自优缺点进行选择: 1.客户端页面采用AJAX技术请求服务器优点:最简单,也最快,就是在返回给客户端的HTML代码中,嵌入AJAX调用,或者,嵌入一 ...
- Atitit. 解决80端口 System 占用pid 4,,找到拉个程序或者服务占用http 80服务
Atitit. 解决80端口 System 占用pid 4,,找到拉个程序或者服务占用http服务 这个是http.sys系统服务占用了... net stop http ,三,没法儿终止 1. 寻 ...
- 基于redis的分布式缓存disgear开源到github上了
disgear是笔者参考solrcloud架构基于redis实现的分布式的缓存,支持数据切分到多台机器上,支持HA,支持读写分离和主节点失效自动选举,目前把它开放到github上,开放给大家 gith ...
- Python 元祖的操作
注意:元祖定义后不可修改,单个元祖后面必须加逗号,否则认为是字符串:tuple = ('apple',) 1.定义元祖 tuple = ('apple','banana','grape','orang ...
- python模块之codecs: 自然语言编码转换
python对多国语言的处理是支持的很好的,它可以处理现在任意编码的字符,这里深入的研究一下python对多种不同语言的处理. 有一点需要清楚的是,当python要做编码转换的时候,会借助于内部 ...
- SenCha Touch HTML 5 应用程序缓存
http://www.cnblogs.com/qidian10/p/3292876.html https://developer.mozilla.org/zh-CN/docs/HTML/Using_t ...
- 数据库 Oracle数据库对象一
常见的数据库对象 表:基本的数据存储集合,由行和列组成 视图:从表中抽出的逻辑上相关的数据集合 序列:提供有规律的数值 索引:提高查询的效率 同义词:给对象起别名 create table语句 --c ...
- 浅谈Unity中的GC以及优化
介绍: 在游戏运行的时候,数据主要存储在内存中,当游戏的数据不在需要的时候,存储当前数据的内存就可以被回收再次使用.内存垃圾是指当前废弃数据所占用的内存,垃圾回收(GC)是指将废弃的内存重新回收再次使 ...
- jquery -- 同时监听多个事件
多个事件触发同一反应 $("#redrow").on("click touchend",function(){});//每个监听事件之间用 “空格” 隔开 多个 ...