Spring Security 5.2 / WebClient使用用户名和密码连接到其他服务的方式是什么?

What's the Spring Security 5.2 / WebClient way of using username & password to connect to another service?

提问人:JeroenV 提问时间:1/14/2020 更新时间:1/16/2020 访问量:3879

问:

我们目前有几个 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 中另一个服务的简单方法?如果是,现在应该使用什么?

java spring-boot,spring-security-oauth2

评论


答:

2赞 SledgeHammer 1/14/2020 #1

是的,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 令牌刷新也是内置的。

评论

0赞 JeroenV 1/14/2020
好吧,首先:Intellij 没有理解的 getHostName(),所以我的猜测是您在同一个文件中有一个额外的方法。其次,在旧代码中,一个client_id(是的,下划线,不是连字符,我刚刚看到)作为属性放在 AccessTokenRequest 上并放在 OAuthClientContext 中,我认为我需要使用它与 client-id 完全相同,第三,我仍然看不到服务帐户的用户名和密码应该放在哪里, 我们不是在处理秘密。顺便说一句,我们正在使用 spring 5.2.2,不知道这是否有所作为
2赞 SledgeHammer 1/14/2020
@JeroenV getHostName() 方法只是我获取主机名的管道。您可以从任何您想要的位置拉取服务器 URL。将用户名和密码放在 client-id 和 client-secret 中。密码授予与客户端凭据授予相同。如果你了解 oAuth 的机制,它们基本上是相同的。是的,我使用的是最新的 Spring / Spring Boot。这是 Spring Security 5 的最新设置。
1赞 SledgeHammer 1/14/2020
@JeroenV To expand on "they are the same", its just Spring Security that puts them in the same place in the application.properties. In reality client credentials vs. password flow grants have identical mechanics, just different key names in the HTTP request.
0赞 JeroenV 1/16/2020 #2

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).

评论

0赞 Glen Mazza 3/15/2021
If one is trying to access Salesforce, this sample worked well for me: reddit.com/r/javahelp/comments/jpzg3o/…