提问人:JeroenV 提问时间:1/14/2020 更新时间:1/16/2020 访问量:3879
Spring Security 5.2 / WebClient使用用户名和密码连接到其他服务的方式是什么?
What's the Spring Security 5.2 / WebClient way of using username & password to connect to another service?
问:
我们目前有几个 Spring Boot 应用程序使用服务帐户连接到其他服务。到目前为止,我们在 RestTemplate 上使用 OAuth2ClientContext 的 AccessTokenRequest 来输入服务帐户的用户和密码,并使用返回的 OAuth 令牌连接到其他服务。
现在我们正在使用 Spring Boot 5.2 构建一个新应用程序,并且由于现在应该使用新的 Spring Security OAuth,因此单独的 OAuth 库已被弃用,我们还希望将 RestTemplate 解决方案替换为 WebClient 解决方案,因为 RestTemplate 也将在不久的将来被弃用。我尝试了几种检索令牌的方法,但找不到有效的解决方案。
我找到了类似于 Spring Security 5 Replacement for OAuth2RestTemplate 中提到的设置,但无法将用户名和密码放入 WebClient 中。 我发现其他方法使用 ClientRegistrationRepository 而不是 ReactiveClientRegistrationRepository,其中一些方法实际上有选项(例如如何在 Spring security 5.2 OAuth 中重新初始化密码授予),将用户名和密码放入 AuthorizedClientManager 中,该用户名和密码在实例化过滤器时成为参数,但不知何故,我总是以找不到 ClientRegistrationRepository 的 Bean 的消息结束, 无论我在application.yaml中放置什么属性(也许这不起作用,因为该应用程序是MVC应用程序而不是WebFlux应用程序?
我知道我需要将 authorization-grant-type 设置为“password”,但是已经有其他人在问如何让它工作(Spring Security 5.2.1 + spring-security-oauth2 + WebClient:如何使用密码授权类型),但还没有这个问题的答案。
所以。。。他们是否“弃用”了这种使用用户名和密码检索令牌并使用该令牌连接到 Spring Security 5.2 中另一个服务的简单方法?如果是,现在应该使用什么?
答:
是的,RestTemplate 和 OAuthRestTemplate 都已弃用。WebClient 支持开箱即用的 OAuth。您无需执行任何特殊操作或自行添加任何标头。这实际上是非常微不足道的。
创建一个公开 WebClient Bean 的配置类,确保将客户端存储库作为参数。在该方法中,将存储库传递到筛选器函数中:
@Bean
public WebClient webClient(ReactiveClientRegistrationRepository clientRegistrations) {
return WebClient.builder().filter(filterFunction(clientRegistrations))
.baseUrl(String.format("http://%s:8080", getHostName()))
.build();
}
private ServerOAuth2AuthorizedClientExchangeFilterFunction filterFunction(ReactiveClientRegistrationRepository clientRegistrations) {
ServerOAuth2AuthorizedClientExchangeFilterFunction filterFunction = new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations, new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
filterFunction.setDefaultClientRegistrationId("myKey");
return filterFunction;
}
注意:在过滤器函数中,将“myKey”替换为与application.properties中的以下属性结构匹配的内容(将属性路径中的myKey替换为您的名称):
spring.security.oauth2.client.registration.myKey.authorization-grant-type=password
spring.security.oauth2.client.registration.myKey.client-id=xxx
spring.security.oauth2.client.registration.myKey.client-secret=xxx
spring.security.oauth2.client.provider.myKey.token-uri=http://localhost:8080/oauth/token
啊......大功告成!OAuth 令牌刷新也是内置的。
评论
Well, it turned out to be a little bit different. A comment on the last SO question I linked requested the author to use debugging in the PasswordOAuth2AuthorizedClientProvider to see what went on / wrong. So I started debugging as well and with the setup you provided there are 4 Providers for 4 login types that are supplied, but out of the 4 it's not the PasswordReactiveOAuth2AuthorizedProvider that's used, and also not the ClientCredentialsReactiveOAuth2AuthorizedProvider or RefreshTokenReactiveOAuth2AuthorizedProvider one, but the AuthorizationCodeReactiveOAuth2AuthorizedProvider is called and that's quite weird when authorization-grant-type is set to password. That seems like a bug to me ...
Anyway, I found another SO question from rigon with a problem a bit different than mine, but close enough: Create route in Spring Cloud Gateway with OAuth2 Resource Owner Password grant type and he provided code I could get working as the Provider used with that codebase is in fact the PasswordReactiveOAuth2AuthorizedClientProvider
In the end I only needed to enter 3 items in the yaml-file: the client-id (with the client_id that used to be put as attribute on the AccessTokenRequest), the authorization-grant-type and the token-uri Besides that I copied the WebClient and ReactiveOAuth2AuthorizedClientManager setup from the code provided by rigon and put the username and password from the configuration file into the settings (I left the seperate contextAttributesMapper out as I only needed to provide 2 context parameters directly into a map).
评论