首页 > 解决方案 > 将请求标头转发到 Spring + Netflix + Feign 中的多个服务调用

问题描述

我的应用程序中有一堆中间和核心服务。所有服务都是 Spring Boot 并使用 Netflix Library。当用户请求信息时,该请求将/可能传递链中的其他服务,例如:

Client <-> Zuul <-> Service B <-> Service A

我已将所有服务(A 和 B)配置为 ResourceServer,以便每次访问都需要进行身份验证。当请求访问令牌(来自 Spring Security Server)并使用它直接从服务 A 请求信息时,一切正常。当我使用相同的令牌访问来自服务 B 的信息(需要服务 A)时,我收到“HTTP 401:需要完整身份验证”错误。服务 B 使用 FeignClient 调用服务 A。

经过一些调试,我发现 Authorization-Header 没有从服务 B 传递给服务 A。服务 B 正确检查令牌本身,授予对该方法的访问权限并尝试执行服务 A 的请求。

我尝试了 RequestInterceptor 但没有任何成功(错误“范围'请求'对当前线程无效”)

@Component
public class OAuth2FeignRequestInterceptor implements RequestInterceptor {
    private static final String AUTHORIZATION_HEADER = "Authorization";
    private static final String BEARER_TOKEN_TYPE = "Bearer";
    private final OAuth2ClientContext oauth2ClientContext;

    public OAuth2FeignRequestInterceptor(OAuth2ClientContext oauth2ClientContext) {
        Assert.notNull(oauth2ClientContext, "Context can not be null");
        this.oauth2ClientContext = oauth2ClientContext;
    }

    @Override
    public void apply(RequestTemplate template) {
        if (template.headers().containsKey(AUTHORIZATION_HEADER)) {
            ...
        } else if (oauth2ClientContext.getAccessTokenRequest().getExistingToken() == null) {
            ...
        } else {
            template.header(AUTHORIZATION_HEADER, String.format("%s %s", BEARER_TOKEN_TYPE,
                    oauth2ClientContext.getAccessTokenRequest().getExistingToken().toString()));
        }
    }
}

这是一个使用 FeignClient 的示例代理函数:

@Autowired
private CategoryClient cat;


@HystrixCommand(fallbackMethod = "getAllFallback", commandProperties = {@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "2") })
@GetMapping("/category")
public ResponseEntity<List<Category>> getAll() {
    try {
        ResponseEntity<List<Category>> categories = this.cat.getAll();
        ...
        return categories;
    } catch(Exception e) {
        ...
    }
}

是否有任何可行的解决方案将 Authorization-Header 从代理功能传递给 FeignClient 以便服务 A 将接收标头并可以对其进行自己的身份验证检查?

标签: javaspring-securitynetflix-feign

解决方案


找到了一个可行的解决方案。我仍然不知道这是否是“最好的”方法,如果有人有更好的解决方案,如果你分享它,我会很高兴。但就目前而言,这正在按预期工作:

@Bean
public RequestInterceptor requestTokenBearerInterceptor() {

    return new RequestInterceptor() {
        @Override
        public void apply(RequestTemplate requestTemplate) {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
            requestTemplate.header("Authorization", "Bearer " + details.getTokenValue());                   
        }
    };
}

推荐阅读