#spring-boot #spring-security #oauth #keycloak #api-gateway
#spring-boot #spring-безопасность #oauth #скрытый ключ #api-шлюз
Вопрос:
Я пытаюсь настроить аутентификацию o auth2 в spring cloud gateway для моих rest api с помощью keycloak. keyclcoak перенаправляет мой запрос на страницу входа при передаче токена доступа в качестве токена предъявителя. Во многих местах я нашел решение для этого — установить bearer-only = true в адаптере keycloak. где установить это при использовании spring-boot-starter-oauth2-client. Я не могу использовать keycloak-spring-boot-starter, чтобы установить это в application.yml
Спасибо
Комментарии:
1. Что заставляет вас использовать spring-security-oauth2-client? Клиентский модуль предназначен для получения токена через поток предоставления OAuth 2.0. Как создаваемый вами шлюз узнает, что это за токен? Если он просто передается шлюзу в
Authorization
заголовке, то spring-security-oauth2-resource-server — это то, что это делает.2. @jzhaux Я пытаюсь выполнить аутентификацию в шлюзе для своих служб, работающих за ним. Я настроил сервер keycloack с областью, пользователями и ролями, с моей конечной точкой токена я могу получить токен oauth2. Я хочу передать этот токен с запросом и пройти аутентификацию в шлюзе. Я новичок в этой spring framework.
3. Спасибо, это полезно. Как вы передаете токен шлюзу? Передает ли вызывающий его шлюзу
Authorization
, например, в заголовке?
Ответ №1:
У меня был тот же вопрос, что и у вас. Не найдя ответа, я разработал фильтр с легким клиентом keycloak, который вызывает конечную точку для проверки keycloak для проверки токена
клиент:
public class KeycloakClient {
private final KeycloakConfigurationProperties kcProperties;
private final WebClient client;
public KeycloakClient(final KeycloakConfigurationProperties keycloakConfigurationProperties) {
this.kcProperties = keycloakConfigurationProperties;
this.client = WebClient.builder()
.baseUrl(keycloakConfigurationProperties.getAuth_server_url())
.filter(logRequest())
.build();
}
public Mono<Boolean> validateBearerToken(String bearerToken) {
//todo: improve error management
return prepareAndRunRequest(bearerToken).retrieve()
.bodyToMono(KeycloakValidationResponse.class)
.flatMap(response -> Mono.just(response.getActive()))
.onErrorResume(WebClientResponseException.class,
ex -> Mono.just(false));
}
private WebClient.RequestHeadersSpec<?> prepareAndRunRequest(String bearerToken) {
return client.post()
.uri(uriBuilder -> uriBuilder.path("/auth/realms/")
.path(kcProperties.getRealm())
.path("/protocol/openid-connect/token/introspect")
.build())
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromFormData("client_id", kcProperties.getResource())
.with("client_secret", kcProperties.getCredentials_secret())
фильтр:
public class ValidationTokenGatewayFilterFactory extends AbstractGatewayFilterFactory<ValidationTokenGatewayFilterFactory.Config> {
private final KeycloakClient client;
public ValidationTokenGatewayFilterFactory(KeycloakClient client) {
super(Config.class);
this.client = client;
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
String token = exchange.getRequest()
.getHeaders()
.get(AUTHORIZATION)
.get(0);
token = token.substring(7);
log.trace("-- ValidationToken(): token={}", token);
return client.validateBearerToken(token)
.flatMap(validated -> {
if (validated) {
log.debug("-- ValidationToken(): Token valid");
return chain.filter(exchange);
} else {
log.debug("-- ValidationToken(): Token invalid");
exchange.getResponse()
.setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse()
.setComplete();
}
});
};
}
public static class Config {
}
}
Вы можете найти полный пример здесь: https://gitlab.com/-/snippets/2105967