我正在尝试解决一个难题,为用于跨服务通信的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
中捡起它.
问题
也许还有其他一些选项,我可以将访问令牌存储在安全上下文中并从那里可靠地获取它,以便不生成新的自定义类?
将很高兴得到任何帮助。
我研究过的一些相关链接:
- 如何从 OAuth2 授权服务器/用户端点获取自定义用户信息 Spring Boot/Spring
- Cloud/Spring Security:如何在计划任务中正确获取 OAuth2 访问令牌
- 春季@FeignClient、OAuth2 和 @Scheduled不起作用
- 如何在 Spring 中为计划进程验证系统用户?
对于历史,希望这能帮助像我这样挣扎的人。
我没有找到比最初更好的方法,并做了一个自定义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);
}
}
我敢打赌这不是最好的解决方案,但到目前为止它似乎工作得相当稳定。
InternalOAuth2Details 已过期