如何将Spring中的路由配置为使用OAuth2客户端和authorization-grant-type: password
?换句话说,如何将带有请求中的令牌的授权头添加到API中?因为我正在与遗留应用程序集成,所以必须使用授予类型密码。
我有这样的申请:
@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>
,一切都很好。
我发现了一个类似的项目:https://github.com/jgrandja/spring-security-oauth-5-2-migrate。它有一个客户机(messaging-client-password
),用于配置WebClient
以添加OAuth2支持以发出请求(即添加授权头)。
我们不能马上使用这个示例项目,因为Spring是反应性的,我们配置东西的方式发生了很大的变化。我认为解决这个问题主要是关于转换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() {
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);
};
}
在按照顺序运行这三个请求之后,我们可以在中使用密码授予。
当然,这个过程是非常混乱的。仍然需要做的是:
contextAttributesMapper
使用凭据初始化授权客户端发布于 2020-05-31 03:33:41
我使用WebClientHttpRoutingFilter
实现了授权-授予类型:密码。
默认情况下,spring云网关使用Netty路由筛选器,但是有一种不需要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
将负责管理访问/刷新令牌,并为您完成所有的艰苦工作。
这里您可以查看此实现。此外,我还使用这种方法实现了客户端凭据授予。
https://stackoverflow.com/questions/59643201
复制相似问题