gpt4 book ai didi

spring - 使用 OAuth2 资源所有者密码授权类型在 Spring Cloud Gateway 中创建路由

转载 作者:行者123 更新时间:2023-12-05 07:15:12 26 4
gpt4 key购买 nike

如何在 Spring Cloud Gateway 中配置路由以使用带有 authorization-grant-type: password 的 OAuth2 客户端?换句话说,如何在 API 请求中添加带有 token 的 Authorization header ?因为我要与遗留应用程序集成,所以我必须使用授权类型密码。

我有这个应用程序:

@SpringBootApplication
public class DemoApplication {

public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("route_path", r -> r.path("/**")
.filters(f -> f.addRequestHeader("Authorization", "bearer <token>"))
.uri("http://localhost:8092/messages"))
.build();
}
}

替换 <token>使用实际 token ,一切正常。

我发现这个项目做类似的事情:https://github.com/jgrandja/spring-security-oauth-5-2-migrate .它有一个客户端(messaging-client-password)用于配置WebClient添加 OAuth2 支持以发出请求(即通过添加授权 header )。

我们不能立即使用这个示例项目,因为 Spring Cloud Gateway 是响应式(Reactive)的,我们配置事物的方式发生了显着变化。我认为解决这个问题主要是关于转换 WebClientConfig类。

更新

我有点让它工作,但它的状态非常糟糕。

首先,我找到了如何转换 WebClientConfig react 灵敏:

@Configuration
public class WebClientConfig {

@Bean
WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth.setDefaultOAuth2AuthorizedClient(true);
oauth.setDefaultClientRegistrationId("messaging-client-password");
return WebClient.builder()
.filter(oauth)
.build();
}

@Bean
ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
ReactiveClientRegistrationRepository clientRegistrationRepository,
ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {

ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
.refreshToken()
.password()
.build();
DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultReactiveOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

// For the `password` grant, the `username` and `password` are supplied via request parameters,
// so map it to `OAuth2AuthorizationContext.getAttributes()`.
authorizedClientManager.setContextAttributesMapper(contextAttributesMapper());

return authorizedClientManager;
}

private Function<OAuth2AuthorizeRequest, Mono<Map<String, Object>>> contextAttributesMapper() {
return authorizeRequest -> {
Map<String, Object> contextAttributes = Collections.emptyMap();
ServerWebExchange serverWebExchange = authorizeRequest.getAttribute(ServerWebExchange.class.getName());
String username = serverWebExchange.getRequest().getQueryParams().getFirst(OAuth2ParameterNames.USERNAME);
String password = serverWebExchange.getRequest().getQueryParams().getFirst(OAuth2ParameterNames.PASSWORD);
if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
contextAttributes = new HashMap<>();

// `PasswordOAuth2AuthorizedClientProvider` requires both attributes
contextAttributes.put(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, username);
contextAttributes.put(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, password);
}
return Mono.just(contextAttributes);
};
}
}

有了这个配置,我们可以使用WebClient提出请求。这会在调用端点后以某种方式初始化 OAuth2 客户端:

@GetMapping("/explicit")
public Mono<String[]> explicit() {
return this.webClient
.get()
.uri("http://localhost:8092/messages")
.attributes(clientRegistrationId("messaging-client-password"))
.retrieve()
.bodyToMono(String[].class);
}

然后,通过调用此方法,我们可以获得对授权客户端的引用:

private OAuth2AuthorizedClient authorizedClient;
@GetMapping("/token")
public String token(@RegisteredOAuth2AuthorizedClient("messaging-client-password") OAuth2AuthorizedClient authorizedClient) {
this.authorizedClient = authorizedClient;
return authorizedClient.getAccessToken().getTokenValue();
}

最后,通过配置全局过滤器,我们可以修改请求以包含授权 header :

@Bean
public GlobalFilter customGlobalFilter() {
return (exchange, chain) -> {
//adds header to proxied request
exchange.getRequest().mutate().header("Authorization", authorizedClient.getAccessToken().getTokenType().getValue() + " " + authorizedClient.getAccessToken().getTokenValue()).build();
return chain.filter(exchange);
};
}

依次运行这三个请求后,我们可以使用 Spring Cloud Gateway 的密码授予。

当然,这个过程很乱。还需要做的事情:

  1. 获取过滤器内授权客户端的引用
  2. 使用 contextAttributesMapper 使用凭据初始化授权客户端
  3. 将所有这些都写在过滤器中,而不是全局过滤器中。 TokenRelayGatewayFilterFactory实现可以为此提供很好的帮助。

最佳答案

我使用 WebClientHttpRoutingFilter 实现了 authorization-grant-type: password。

默认情况下,spring cloud gateway 使用 Netty Routing Filter 但有一个替代方案不需要 Netty ( https://cloud.spring.io/spring-cloud-gateway/reference/html/#the-netty-routing-filter )

WebClientHttpRoutingFilter 使用 WebClient 来路由请求。

WebClient 可以通过 ExchangeFilterFunction ( https://docs.spring.io/spring-security/site/docs/current/reference/html5/#webclient ) 使用 ReactiveOAuth2AuthorizedClientManager 配置。 ReactiveOAuth2AuthorizedClientManager 将负责管理访问/刷新 token 并将为您完成所有繁重的工作

Here您可以查看此实现。此外,我用这种方法实现了客户端凭证授予

关于spring - 使用 OAuth2 资源所有者密码授权类型在 Spring Cloud Gateway 中创建路由,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59643201/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com