#sprin& #oauth-2.0 #sprin&-webflux #sprin&-security-oauth2
#sprin& #oauth-2.0 #sprin&-webflux #sprin&-security-oauth2
Вопрос:
У меня есть приложение sprin&, которое проверяет токен JWT на конечной точке rest.
Использование SecurityChain
.oAuth2ResourceServer()
.jwt()
Похоже, это создает JwtAuthenticationToken
в ReactiveSecurityContextHolder
.
Затем я хочу передать входные данные с этой конечной точки, где клиент аутентифицируется путем проверки токена предъявителя. А затем вызовите другую службу rest, используя webClient
. Этому веб-клиенту необходимо пройти аутентификацию с помощью пароля типа &rant во внешней службе, используя другой сервер OAuth, и получить собственный токен на предъявителя.
Проблема в том, что веб-клиент использует ReactiveSecurityContextHolder
, который содержит аутентифицированный JWT. И пытается использовать эту информацию, а не подключаться и аутентифицировать мое приложение к конечной точке rest.
Я настроил Yaml для регистрации моего клиента
sprin&:
security:
oauth2:
client:
re&istration:
Myapp:
client-id:
client-secret:
token-uri:
authorization-&rant-type:
Затем добавление функции фильтрации
ServerOAuth2AuthorizedClientExchan&eFilterFunction
Но я получаю, principalName cannot be empty
поскольку это, похоже, повторно использует контекст безопасности при проверке вызывающего на конечной точке rest в моем приложении.
Как это должно быть спроектировано или примеры, чтобы показать, как вы можете использовать разные контексты безопасности или получать токены по-разному между вызовами от службы к службе?
Комментарии:
1. Не могли бы вы, пожалуйста, пояснить, как вы получите имя пользователя и пароль владельца ресурса? Исходя из вашего объяснения, похоже, что ваш REST API получает только JWT в запросе.
2. Я предоставлю их при запуске. Поэтому я не хочу, чтобы клиент, чей JWT мы проверяем, затем аутентифицировался в службе, которую мы вызываем с помощью WebClient. Я буду проходить аутентификацию с помощью WebClient как клиента OAuth по своему собственному идентификатору клиента приложения.
3. Тогда было бы более подходящим предоставление учетных данных клиента?
4. Я должен использовать предоставление пароля. Но проблема была бы той же самой. Поскольку я хотел бы снова пройти аутентификацию, используя учетные данные клиента. Но, похоже, он хочет использовать аутентификацию JWT.
5. Есть еще кое-что, чего мне пока не хватает в объяснении.
oauth2ResourceServer().jwt()
создастJwtAuthenticationToken
, но вы говорите, что получаетеOAuth2TokenAuthenticationToken
. Во-первых, вы имеете в видуOAuth2AuthenticationToken
? И, во-вторых, есть ли какие-либо дополнительные настройки безопасности OAuth 2.0 Sprin&, которые выполняет ваше приложение?
Ответ №1:
Вы правы в том, что дизайн ServerOAuth2AuthorizedClientExchan&eFilterFunction
разработан на основе текущего авторизованного клиента, который, как вы объяснили, вы не хотите использовать в этом случае.
Вы указали, что хотите использовать учетные данные клиента в качестве имени пользователя и пароля для предоставления пароля владельца ресурса. Однако в Sprin& Security нет ничего, что могло бы это сделать.
Однако вы можете использовать WebClientReactivePasswordTokenResponseClient
напрямую, чтобы самостоятельно сформулировать пользовательский запрос.
Вкратце, это был бы пользовательский Exchan&eFilterFunction
способ, который выглядел бы примерно так:
ClientRe&istrationRespository clientRe&istrations;
ReactiveOAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest&&t;
accessTokenResponseClient = new WebClientReactivePasswordTokenResponseClient();
Mono<ClientResponse&&t; filter(ClientRequest request, Exchan&eFunction next) {
return this.clientRe&istrations.findByRe&istrationId("re&istration-id")
.map(clientRe&istration -&&t; new OAuth2PasswordGrantRequest(
clientRe&istration,
clientRe&istration.&etClientId(),
clientRe&istration.&etClientSecret())
.map(this.accessTokenResponseClient::&etTokenResponse)
.map(tokenResponse -&&t; ClientRequest.from(request)
.headers(h -&&t; h.setBearerAuth(tokenResponse.&etAccessToken().&etTokenValue())
.build())
.flatMap(next::exchan&e);
}
(Для краткости я удалил любую обработку ошибок.)
Приведенный выше код выполняет следующие шаги:
- Найдите соответствующую регистрацию клиента — она содержит конечную точку поставщика, а также идентификатор клиента и секрет
- Создайте
OAuth2PasswordGrantRequest
, используя идентификатор клиента и secret в качестве имени пользователя и пароля владельца ресурса - Выполните запрос, используя
WebClientReactivePasswordTokenResponseClient
- Установите токен доступа в качестве токена-носителя для запроса
- Перейдите к следующей функции в цепочке
Обратите внимание, что для использования клиентских функций Sprin& Security OAuth 2.0 вам также потребуется настроить свое приложение в качестве клиента. Это означает, по крайней мере, изменение вашего DSL на включение .oauth2Client()
в дополнение к .oauth2ResourceServer()
. Это также будет означать настройку ClientRe&istrationRepository
. Чтобы мой комментарий был сосредоточен на функциях фильтрации, я опустил эту деталь, но я был бы рад помочь и здесь, если необходимо.
Комментарии:
1. Большое спасибо! Просматривал PasswordTokenResponseClient и, похоже, заставил его работать. Попробуем предложенный вами шаблон вставки его в фильтр и обновления.
2. Спасибо, выглядит неплохо! Я должен получить обновление токена бесплатно, верно?
3. Это зависит от поставщика: tools.ietf.or&/html/rfc6749#section-4.3.3