Spring Security 5.1 - 使用 WebClient 获取客户端凭证流的令牌
Posted
技术标签:
【中文标题】Spring Security 5.1 - 使用 WebClient 获取客户端凭证流的令牌【英文标题】:Spring Security 5.1 - Get Token for Client Credentials Flow with WebClient 【发布时间】:2019-04-10 23:09:03 【问题描述】:我正在尝试通过 webclient
获取不记名令牌,并使用以下设置对 servlet 应用程序中的安全资源服务器进行集成测试。
spring:
security:
oauth2:
client:
registration:
idp:
clientId: id
clientSecret: secret
authorization-grant-type: client_credentials
scope: read
provider:
idp:
authorization-uri: myidp/authorization.oauth2
token-uri: myidp/token.oauth2
user-info-uri: myidp/userinfo.openid
user-name-attribute: name
还有豆子,
@Bean
WebClient webClient(ClientRegistrationRepository clientRegistrations,
OAuth2AuthorizedClientRepository authorizedClients)
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServletOAuth2AuthorizedClientExchangeFilterFunction(
clientRegistrations, authorizedClients);
// (optional) explicitly opt into using the oauth2Login to provide an access token implicitly
// oauth.setDefaultOAuth2AuthorizedClient(true);
// (optional) set a default ClientRegistration.registrationId
// oauth.setDefaultClientRegistrationId("client-registration-id");
return WebClient.builder().apply(oauth.oauth2Configuration()).build();
并将 web 客户端自动装配到测试并像这样调用它,
webClient.get().uri("http://localhost:" + port + "/web/it")
.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId("idp")).retrieve()
.bodyToMono(String.class).block();
我的假设是交换函数要么获取访问令牌(如果可用),要么调用从 IDP 获取新令牌。但是它总是会失败,因为HttpSessionOAuth2AuthorizedClientRepository
因为HttpServletRequest
为空。
整体测试看起来像这样,这将提供给自动配置,以便为 IDP 提供者配置一些 bean。
@SpringBootTest(classes = WebITApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ExtendWith(SpringExtension.class)
@ActiveProfiles("web-it")
class WebJwtIt
@LocalServerPort
private int port;
@Autowired
private WebClient webClient;
@Test
void testIdpJwt()
String response = webClient.get().uri("http://localhost:" + port + "/web/it")
.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId("ping")).retrieve()
.bodyToMono(String.class).block();
assertThat(response).isEqualTo("pass");
@RestController
@SpringBootApplication
@ImportAutoConfiguration(IdpAutoConfiguration.class)
static class WebITApplication implements IdpSecurityAdapter
@Bean
WebClient webClient(ClientRegistrationRepository clientRegistrations,
OAuth2AuthorizedClientRepository authorizedClients)
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServletOAuth2AuthorizedClientExchangeFilterFunction(
clientRegistrations, authorizedClients);
// (optional) explicitly opt into using the oauth2Login to provide an access token implicitly
// oauth.setDefaultOAuth2AuthorizedClient(true);
// (optional) set a default ClientRegistration.registrationId
// oauth.setDefaultClientRegistrationId("client-registration-id");
return WebClient.builder().apply(oauth.oauth2Configuration()).build();
public static void main(String args[])
new SpringApplicationBuilder().profiles("web-it").build().run(WebITApplication.class, args);
@GetMapping
public String secured()
return "secured";
@GetMapping("/web/it")
public String securedOne()
return "pass";
@Override
public void configure(final HttpSecurity httpSecurity) throws IdpSecurityAdapterException
try
httpSecurity.oauth2Client();
catch (Exception e)
throw new IdpSecurityAdapterException("Failed to Configure Oauth2Client", e);
@Override
public IdpProvider getIdpProvider()
return IdpProvider.MyIdp;
无论如何,网络客户端是否可以为我获取令牌并将其添加到请求中?我知道spring-security-oauth:OAuthRestTemplate
可以做到这一点,并且阅读了我认为通过网络客户端可以做到的文档。
【问题讨论】:
【参考方案1】:这里的问题是您没有以正确的方式实例化您的 WebClient。
由于您在客户端,您无权访问OAuth2AuthorizedClientRepository
。该 bean 应该链接到您使用 HttpSecurity
configuration 上的 .oauth2Login()
方法声明登录的资源服务器。这些细节在这里解释:Spring Security 5 Oauth2 Login。
同样,您在客户端,因此您需要一个交换过滤器功能,该功能将触发对授权服务器的请求以获取 JWT 令牌。您可以改用ServerOAuth2AuthorizedClientExchangeFilterFunction
。
您最好使用WebClientCustomizer
在 WebClient 过滤器中添加交换过滤器功能。为什么 ?仅仅因为在您的 Spring 应用程序中注入 WebClient.Builder
将允许您访问链接到 Web 交换的本机指标。
因此,您将使用UnAuthenticatedServerOAuth2AuthorizedClientRepository
bean 的新实例来构建您的 WebClient,如下所示:
// Use injection to get an in-memory reposiroty or client registrations
@Bean
WebClient webClient(ClientRegistrationRepository clientRegistrations)
// Provides support for an unauthenticated user such as an application
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(
clientRegistrations, new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
// Build up a new WebClientCustomizer implementation to inject the oauth filter
// function into the WebClient.Builder instance
return new WebClientSecurityCustomizer(oauth);
由于您在客户端,您没有将用户关联到您的流程,这就是为什么您不能使用任何授权的客户端存储库 bean 实例化。查看 Spring Security 文档:Spring Security documentation : Class UnAuthenticatedServerOAuth2AuthorizedClientRepository。
我尝试在以下GitHub项目中总结一个演示案例:GitHub - Spring Security OAuth2 Machine-To-Machine scenario。
我希望这能让您对 WebClient 配置有更多的了解。如果您有任何问题,请询问。
【讨论】:
这太棒了,到现在一定错过了这个回复! 如果您在 Servlet 环境中的后台线程/非 http 线程中,只想为任何读者添加 ***.com/questions/55308918/… 也应该清楚这一点。使用 Spring Security 5.2 可以使用适当的配置。 我的 Web 客户端有问题,它会在调用资源服务器之前为每个请求获取一个访问令牌。你已经测试了吗? 很好的解释:)以上是关于Spring Security 5.1 - 使用 WebClient 获取客户端凭证流的令牌的主要内容,如果未能解决你的问题,请参考以下文章
全网最新Spring Boot2.5.1整合Activiti5.22.0企业实战教程<入门篇>
如何使用带有 WebClient 的 spring-security-oauth2 自定义 OAuth2 令牌请求的授权标头?
Spring Boot Actuator Endpoints 安全性不适用于自定义 Spring Security 配置
Spring Framework,Spring Security - 可以在没有 Spring Framework 的情况下使用 Spring Security?