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

编程入门 行业动态 更新时间:2024-10-27 19:29:48
本文介绍了使用OAuth2资源所有者密码授予类型在Spring Cloud Gateway中创建路由的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

如何在Spring Cloud Gateway中配置路由以将OAuth2客户端与authorization-grant-type: password一起使用?换句话说,如何在对API的请求中添加带有令牌的Authorization标头?因为我正在与旧版应用程序集成,所以必须使用授权类型密码.

How to configure a route in Spring Cloud Gateway to use an OAuth2 client with authorization-grant-type: password? In other words, how to add the Authorization header with the token in the requests to an API? Because I'm integrating with a legacy application, I must use the grant type password.

我有这个应用程序:

@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("localhost:8092/messages")) .build(); } }

将<token>替换为实际的令牌,一切正常.

Replacing the <token> with an actual token, everything just works fine.

我发现该项目具有类似的功能: github/jgrandja/spring-security-oauth-5-2-migrate .它具有一个客户端(messaging-client-password),该客户端用于配置WebClient以添加OAuth2支持以发出请求(即通过添加Authorization标头).

I found this project that does something similar: github/jgrandja/spring-security-oauth-5-2-migrate. It has a client (messaging-client-password) that is used to configure the WebClient to add OAuth2 support to make requests (i.e. by adding the Authorization header).

我们无法立即使用此示例项目,因为Spring Cloud Gateway是反应性的,并且我们配置事物的方式发生了巨大变化.我认为解决这个问题主要是关于转换 WebClientConfig 类.

We can't use this sample project right away because Spring Cloud Gateway is reactive and the way we configure things changes significantly. I think to solve this problem is mostly about converting the WebClientConfig class.

更新

我有点使它起作用,但是它的形状非常糟糕.

I kinda make it work, but it is in very bad shape.

首先,我发现了如何将WebClientConfig转换为反应性的:

First, I found how to convert WebClientConfig to be reactive:

@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客户端:

With this configuration, we can use the WebClient to make a request. This somehow initializes the OAuth2 client after calling the endpoint:

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

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

Then, by calling this one we are able to get the reference to the authorized client:

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

最后,通过配置全局过滤器,我们可以修改请求以包括Authorization标头:

And finally, by configuring a global filter, we can modify the request to include the Authorization 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中使用密码授予.

After running this three requests in order, we can use the password grant with Spring Cloud Gateway.

当然,此过程非常混乱.仍然需要做的事情:

Of course, this process is very messy. What still needs to be done:

  • 获取过滤器中授权客户的参考
  • 使用contextAttributesMapper
  • 使用凭据初始化授权的客户端
  • 将所有这些内容写在过滤器中,而不是全局过滤器中. TokenRelayGatewayFilterFactory 实现可以为实现这一目标提供很好的帮助.
  • Get the reference for the authorized client inside the filter
  • Initialize the authorized client with the credentials using contextAttributesMapper
  • Write all of this in a filter, not in a global filter. TokenRelayGatewayFilterFactory implementation can provide a good help to do this.
  • 推荐答案

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

    I implemented authorization-grant-type: password using WebClientHttpRoutingFilter.

    默认情况下,Spring云网关使用Netty路由过滤器,但有一种替代方案不需要Netty( cloud.spring.io/spring-cloud-gateway/reference/html/#the-netty-routing-filter )

    By default, spring cloud gateway use Netty Routing Filter but there is an alternative that not requires Netty (cloud.spring.io/spring-cloud-gateway/reference/html/#the-netty-routing-filter)

    WebClientHttpRoutingFilter使用WebClient路由请求.

    WebClient可以通过ExchangeFilterFunction( docs.spring.io/spring-security/site/docs/current/reference/html5/#webclient ). ReactiveOAuth2AuthorizedClientManager将负责管理访问/刷新令牌,并将为您完成所有辛苦的工作

    The WebClient can be configured with a ReactiveOAuth2AuthorizedClientManager through of an ExchangeFilterFunction (docs.spring.io/spring-security/site/docs/current/reference/html5/#webclient). The ReactiveOAuth2AuthorizedClientManager will be responsible of management the access/refresh tokens and will do all the hard work for you

    此处,您可以查看此实现.另外,我通过这种方法实现了客户凭证授予

    Here you can review this implementation. In addition, I implemented the client-credentials grant with this approach

    更多推荐

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

    本文发布于:2023-11-04 21:54:46,感谢您对本站的认可!
    本文链接:https://www.elefans.com/category/jswz/34/1559115.html
    版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
    本文标签:所有者   路由   密码   类型   资源

    发布评论

    评论列表 (有 0 条评论)
    草根站长

    >www.elefans.com

    编程频道|电子爱好者 - 技术资讯及电子产品介绍!