Я пытаюсь решить головоломку, включив аутентификацию на основе OAuth2 для моего клиента Feign, который используется для связи между службами.
В обычных случаях, когда пользователь отправляет запрос через API, я могу взять все данные аутентификации, необходимые для SecurityContextHolder
Spring (поскольку он обычно выполняет свою работу и заполняет все объекты деталей) и улучшить Request
Feign следующим образом:
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 в запланированной задаче
- Spring @FeignClient, OAuth2 и @Scheduled не работают
- Как я могу аутентифицировать системного пользователя для запланированные процессы в Spring?