使用Feign为计划的跨服务任务启用OAuth2

时间:2017-12-29 15:21:54

标签: java spring spring-security oauth spring-cloud-feign

我尝试通过为我的Feign客户端启用基于OAuth2的身份验证来解决难题,该客户端用于跨服务通信。

在正常情况下,当用户通过API推送请求时,我能够获取Spring SecurityContextHolder所需的所有身份验证详细信息(因为它通常会执行其工作并填写所有内容)细节对象)并增强Feign的Request如下:

public class FeignAccessTokenRelyInterceptor implements RequestInterceptor {
    private static final Logger log = LoggerFactory.getLogger(FeignAccessTokenRelyInterceptor.class);

    @Override
    public void apply(RequestTemplate template) {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null){
            String tokenValue = null;
            if (auth.getDetails() instanceof OAuth2AuthenticationDetails) {
                OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) auth.getDetails();
                tokenValue = details.getTokenValue();
            }
            if (tokenValue == null) {
                log.warn("Current token value is null");
                return;
            }
            template.header("Authorization", "Bearer " + tokenValue);
        }
    }
}

但是,当涉及在系统内触发的预定呼叫时,SecurityContext显然是空的。我通过预先通过客户端凭据流手动请求访问令牌并加载用户详细信息来填充UserInfoTokenServices

OAuth2Authentication authentication = userInfoTokenServices.loadAuthentication(accessToken);
SecurityContextHolder.getContext().setAuthentication(authentication);

但是这样的构造并没有填充OAuth2Authentication.details,我依靠它来获取访问令牌。我尝试扩展OAuth2AuthenticationDetails,但唯一的构造函数需要HttpServletRequest,这很难进入计划任务,并且创建它的虚拟实例感觉是一个糟糕的选择。

到目前为止,我看到了唯一足够的选项,可以对详细信息持有者进行单独的自定义实现,并将其与我拥有的访问令牌一起传递给OAuth2Authentication。然后在FeignAccessTokenRelyInterceptor中找到它。

问题

也许还有一些其他选项可以将我的访问令牌存储在安全上下文中并可靠地从那里获取,以便不生成新的自定义类?

很高兴得到任何帮助。

我研究过的一些相关链接:

1 个答案:

答案 0 :(得分:0)

对于历史,希望能帮助像我这样的人在努力解决这个问题。

我找不到比初始方法更好的方法,并使用自定义InternalOAuth2Details来保存从Spring的OAuth服务获得的标记值。然后,在FeignAccessTokenRelyInterceptor中,我只需检查当前详细信息是否为InternalOAuth2Details并尝试从中获取令牌值,如下所示:

@Override
public void apply(RequestTemplate template) {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();

    if (auth != null){
        String tokenValue = null;
        if (auth.getDetails() instanceof OAuth2AuthenticationDetails) {
            OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) auth.getDetails();
            tokenValue = details.getTokenValue();
        } else if (auth.getDetails() instanceof InternalOAuth2Details) {
            InternalOAuth2Details details = (InternalOAuth2Details) auth.getDetails();
            tokenValue = details.getTokenValue();
        }
        if (tokenValue == null) {
            log.warn("Current token value is null");
            return;
        }
        template.header("Authorization", "Bearer " + tokenValue);
    }
}

我敢打赌这不是最好的解决方案,但它现在似乎相当稳定。