当前位置: 首页 > 知识库问答 >
问题:

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

桓嘉谊
2023-03-14

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

我有这个申请:

@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();
    }
}

用一个实际的令牌替换 ,一切正常。

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

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

@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发出请求。这在调用endpoint后以某种方式初始化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();
}
@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);
    };
}
  1. 在筛选器中获取授权客户端的引用
  2. 使用ContextAttributesMapper
  3. 用凭据初始化授权客户端
  4. 将所有这些都写入筛选器,而不是全局筛选器。TokenRelayGatewayFilterFactory实现可以提供很好的帮助。

共有1个答案

晏兴发
2023-03-14

我使用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)配置ReactiveOAuth2AuthorizedClientManagerReactiveOAuth2AuthorizedClientManager将负责管理访问/刷新令牌,并为您完成所有艰巨的工作

在这里,您可以回顾这个实现。此外,我用这种方法实现了客户机凭据授予

 类似资料:
  • 应用程序需要连接到仅支持OAuth2资源所有者密码凭据授予的API。我尝试使用下面的代码,但得到的响应代码为400“错误请求”。使用相同的代码,我可以连接到一个正常的网站并检索内容。 我知道API代码是工作,因为使用邮递员工作。在邮递员中,我只是发出一个帖子请求,提供用户名、密码和grant_type,并使用x-www. form-urlencoded 连接的返回是一个json。 你知道怎么了吗?

  • 本文向大家介绍oauth 资源所有者密码凭证授予,包括了oauth 资源所有者密码凭证授予的使用技巧和注意事项,需要的朋友参考一下 示例 资源

  • 关于使用OAuth2的spring-cloud-security我有两个问题: > 如何通过grant_type保护资源?我想用client_credentials保护我的REST API,然后通过密码授权类型保护用户特定的资源。这可能吗? 与本教程相一致的是,密码授予类型不需要客户端秘密,尽管spring cloud oauth2似乎总是需要客户端秘密。我怎么处理这个?通过设置一个像“ABC”这

  • null 用户在登录表单中输入凭据->应用程序1将使用用户凭据从应用程序2获取令牌,其具有密码授予类型->使用令牌访问应用程序1的资源。 问题是Spring Security 5是否支持客户端的密码授予类型?我在Spring Security 5实现中找到了所有rest授予类型,但没有找到密码。

  • 资源所有者密码凭据(即用户名和密码),可以直接作为获取访问令牌的授权许可。这种凭据只能应该当资源所有者和客户端之间具有高度信任时(例如,客户端是设备的操作系统的一部分,或者是一个高度特权应用程序),以及当其他授权许可类型(例如授权码)不可用时被使用。 尽管本授权类型需要对资源所有者凭据直接的客户端访问权限,但资源所有者凭据仅被用于一次请求并被交换为访问令牌。通过凭据和长期有效的访问令牌或刷新令牌的

  • 我一直在广泛阅读有关OAuth和OpenID Connect的内容,但此问题专门涉及OAuth2资源所有者密码授予(又名OAuth2资源所有者凭据授予,又名OAuth2密码授予) 某些资源(例如Justin Richer的《OAuth2 in Action》一书)说不要使用OAuth2资源所有者密码授予进行身份验证-请参阅书中的第6.1.3节。 以下其他好资源都说我们可以使用OAuth2资源所有者