使用 Feign 客户端 RequestInterceptor 转发请求 header 或安全上下文
Forward a request header or the security context with a Feign client RequestInterceptor
我想用假客户端 RequestInterceptor 转发请求 header,但是在 RequestInterceptor.apply
内,RequestContextHolder.getRequestAttributes()
是 null
,SecurityContextHolder.getContext().getAuthentication()
也是(在那里我最终也可以获得我的 header 的价值)。
这在升级到 Spring-Cloud Brixton 之前是有效的,其中 hystrix 命令现在可能 运行 在一个单独的线程中,因为更改为以下参数可以解决问题:
hystrix.command.default.execution.isolation.strategy: SEMAPHORE
现在,如果没有必要,我不太热衷于更改这种默认值,现在有另一种推荐的转发方式 headers 吗?
谢谢
对于 Spring Boot 2+ / Spring Cloud Finchley +,如果您只需要安全上下文,您可以设置以下内容 属性 :
hystrix.shareSecurityContext=true
并且请求拦截器应该可以工作。
对于其他用例或早期版本(非常感谢 Spring Cloud Sleuth 的启发):
"All" 你必须做的是实现一个 HystrixConcurrencyStrategy
,它在每次线程更改时传递信息。 class 在 Sleuth 中做了非常相似的事情是 here。
对于我的具体情况,我会:
- 将
Callable
包裹在 wrapCallable
中,例如,CallableWithAuthentication
class 将在构造时保存当前身份验证
CallableWithAuthentication
call
方法会先恢复之前保存的Authentication
,然后调用原来的action,然后清理当前的Authentication
,等等。
一旦您的 HystrixConcurrencyStrategy
启动,您的请求拦截器将再次工作,即使有线程隔离。
注意检查项目的其余部分,还有许多其他有趣的工具(例如 RxJava)。
只是为了详细说明@sebastian 的回答
public class RequestContextHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
@Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
return new RequestAttributeAwareCallable<>(callable, RequestContextHolder.getRequestAttributes());
}
static class RequestAttributeAwareCallable<T> implements Callable<T> {
private final Callable<T> delegate;
private final RequestAttributes requestAttributes;
public RequestAttributeAwareCallable(Callable<T> callable, RequestAttributes requestAttributes) {
this.delegate = callable;
this.requestAttributes = requestAttributes;
}
@Override
public T call() throws Exception {
try {
RequestContextHolder.setRequestAttributes(requestAttributes);
return delegate.call();
} finally {
RequestContextHolder.resetRequestAttributes();
}
}
}
}
然后在配置的某处
@PostConstruct
public void init() {
HystrixPlugins.getInstance().registerConcurrencyStrategy(new RequestContextHystrixConcurrencyStrategy());
}
我想用假客户端 RequestInterceptor 转发请求 header,但是在 RequestInterceptor.apply
内,RequestContextHolder.getRequestAttributes()
是 null
,SecurityContextHolder.getContext().getAuthentication()
也是(在那里我最终也可以获得我的 header 的价值)。
这在升级到 Spring-Cloud Brixton 之前是有效的,其中 hystrix 命令现在可能 运行 在一个单独的线程中,因为更改为以下参数可以解决问题:
hystrix.command.default.execution.isolation.strategy: SEMAPHORE
现在,如果没有必要,我不太热衷于更改这种默认值,现在有另一种推荐的转发方式 headers 吗?
谢谢
对于 Spring Boot 2+ / Spring Cloud Finchley +,如果您只需要安全上下文,您可以设置以下内容 属性 :
hystrix.shareSecurityContext=true
并且请求拦截器应该可以工作。
对于其他用例或早期版本(非常感谢 Spring Cloud Sleuth 的启发):
"All" 你必须做的是实现一个 HystrixConcurrencyStrategy
,它在每次线程更改时传递信息。 class 在 Sleuth 中做了非常相似的事情是 here。
对于我的具体情况,我会:
- 将
Callable
包裹在wrapCallable
中,例如,CallableWithAuthentication
class 将在构造时保存当前身份验证 CallableWithAuthentication
call
方法会先恢复之前保存的Authentication
,然后调用原来的action,然后清理当前的Authentication
,等等。
一旦您的 HystrixConcurrencyStrategy
启动,您的请求拦截器将再次工作,即使有线程隔离。
注意检查项目的其余部分,还有许多其他有趣的工具(例如 RxJava)。
只是为了详细说明@sebastian 的回答
public class RequestContextHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
@Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
return new RequestAttributeAwareCallable<>(callable, RequestContextHolder.getRequestAttributes());
}
static class RequestAttributeAwareCallable<T> implements Callable<T> {
private final Callable<T> delegate;
private final RequestAttributes requestAttributes;
public RequestAttributeAwareCallable(Callable<T> callable, RequestAttributes requestAttributes) {
this.delegate = callable;
this.requestAttributes = requestAttributes;
}
@Override
public T call() throws Exception {
try {
RequestContextHolder.setRequestAttributes(requestAttributes);
return delegate.call();
} finally {
RequestContextHolder.resetRequestAttributes();
}
}
}
}
然后在配置的某处
@PostConstruct
public void init() {
HystrixPlugins.getInstance().registerConcurrencyStrategy(new RequestContextHystrixConcurrencyStrategy());
}