如何将Spring中的路由配置为使用OAuth2客户端和 authorization-grant-type: password ?换句话说,如何将带有请求中的令牌的授权头添加到API中?因为我正在与遗留应用程序集成,所以必须使用授予类型密码。
authorization-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("http://localhost:8092/messages")) .build(); }
用一个实际的令牌替换 <token> ,一切都很好。
<token>
我发现了一个类似的项目: https://github.com/jgrandja/spring-security-oauth-5-2-migrate 。它有一个客户机( messaging-client-password ),用于配置 WebClient 以添加OAuth2支持以发出请求(即添加授权头)。
messaging-client-password
WebClient
我们不能马上使用这个示例项目,因为Spring是反应性的,我们配置东西的方式发生了很大的变化。我认为解决这个问题主要是关于转换 WebClientConfig 类。
更新
我让它起作用了,但它的状态很差。
首先,我发现了如何将 WebClientConfig 转换为反应性的:
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 发出请求。这在调用端点之后以某种方式初始化了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() {