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

Enabling OAuth2 with Feign for scheduled cross-service tasks

我正在尝试通过为用于跨服务通信的 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.

捡起来

问题

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

很乐意提供任何帮助。

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

对于历史,希望这能帮助像我这样苦苦挣扎的人。

我没有找到比最初的方法更好的方法,因此自定义了 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);
    }
}

我敢打赌这不是最好的解决方案,但到目前为止它似乎工作得相当稳定。