当前位置: 首页 > 知识库问答 >
问题:

具有密码授予类型的WebClient的Spring Security OAuth客户端不请求新令牌

马阳曦
2023-03-14

我有一个Spring Web应用程序,它有一个为它的APIendpoint配置的oauth2资源服务器和一个完全不同的用于REST调用的oauth2客户端。oAuth2客户端需要具有密码授予类型。用户名和密码是固定的(不是来自HTTP请求)。我的问题是30分钟后访问令牌和刷新令牌都过期了,所以没有办法刷新。我希望Spring Security只会要求新的访问令牌,但没有。它用过期的endpoint调用RESTendpoint,并返回403。这是我所拥有的:

application.yml:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://our.idp.keycloak.host/auth/realms/firstrealm
      client:
        registration:
          my-client-authorization:
            client-id: my_client
            client-secret: ${CLIENT_SECRET}
            authorization-grant-type: password
            scope: openid, profile
        provider:
          my-client-authorization:
            token-uri: https://our.idp.keycloak.host/auth/realms/secondrealm/protocol/openid-connect/token

MyClientConfig。爪哇:

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.security.oauth2.client.OAuth2AuthorizationContext;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;

import java.util.Map;

@Configuration
@RequiredArgsConstructor
public class MyClientConfig {
    @Bean
    WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
        ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client = new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
        oauth2Client.setDefaultClientRegistrationId("my-client-authorization");
        return WebClient.builder()
                .apply(oauth2Client.oauth2Configuration())
                .baseUrl("https://the.api.host.to.call")
                .build();
    }

    @Bean
    public OAuth2AuthorizedClientManager authorizedClientManager(
            ClientRegistrationRepository clientRegistrationRepository,
            OAuth2AuthorizedClientRepository authorizedClientRepository
    ) {
        OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
                .password()
                .build();

        DefaultOAuth2AuthorizedClientManager result = new DefaultOAuth2AuthorizedClientManager(
                clientRegistrationRepository,
                authorizedClientRepository
        );

        result.setAuthorizedClientProvider(authorizedClientProvider);
        result.setContextAttributesMapper(oAuth2AuthorizeRequest -> Map.of(
                OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, "user",
                OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, "password"
        ));

        return result;
    }
}

API调用本身:

private <T> T callApi(Function<UriBuilder, URI> uriFunction, Class<T> resultType) {
    return this.webClient
            .get()
            .uri(uriFunction)
            .retrieve()
            .bodyToMono(resultType)
            .block();
}

我第一次给它打电话时它就起作用了。但30分钟后,代币就没了,我不知道怎么换一个新的。如果我将其切换为client_credentials grant类型,它就会工作,在需要时会自动获取一个新令牌。但由于某些原因,我无法对密码授予类型执行相同的操作。

编辑:所以我设法解决了这个问题,感谢这个:https://github.com/spring-projects/spring-security/issues/8831。当我配置WebClient也用于刷新令牌时,当刷新令牌过期时,它会崩溃。但是当您在此之后再次执行请求时,它将获得一个新的令牌。因此,我必须包装的API调用中的试抓,如果错误是我关心的,我再次调用API。这不是一个非常优雅的解决方案,但它是有效的。

共有1个答案

王才英
2023-03-14

这里清楚地提到,Spring会在访问令牌过期时自动刷新它。

我认为您需要您所使用的类的“反应性”等效类。请尝试这个片段。

@Configuration
@RequiredArgsConstructor
public class MyClientConfig {
  @Bean
  WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
    ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client = new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
    oauth2Client.setDefaultClientRegistrationId("my-client-authorization");
    return WebClient.builder()
        .filter(oauth2Client)
        .baseUrl("https://the.api.host.to.call")
        .build();
  }

  @Bean
  public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
      ReactiveClientRegistrationRepository clientRegistrationRepository,
      ReactiveOAuth2AuthorizedClientService authorizedClientService
  ) {
    ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
        .password()
        .build();

    AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager result = new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
        clientRegistrationRepository,
        authorizedClientService
    );

    result.setAuthorizedClientProvider(authorizedClientProvider);
    result.setContextAttributesMapper(oAuth2AuthorizeRequest -> Mono.just(Map.of(
        OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, "user",
        OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, "password"
    )));

    return result;
  }
}
 类似资料:
  • null 用户在登录表单中输入凭据->应用程序1将使用用户凭据从应用程序2获取令牌,其具有密码授予类型->使用令牌访问应用程序1的资源。 问题是Spring Security 5是否支持客户端的密码授予类型?我在Spring Security 5实现中找到了所有rest授予类型,但没有找到密码。

  • 问题内容: 我正在使用一组受oAuth2保护的服务。当前它是这样工作的:客户端使用其用户名和密码登录。我用这些交换令牌。我将令牌保存在会话中,并在每次调用服务时提交。它可以工作,但是问题是我完全手动执行此操作,而没有使用很多Spring Security oAuth2支持。外观如下: 如您所见,我自己创建了身份验证提供程序。它正在接受该标准,但正在产生自己的扩展,该扩展也保留了实际内容,因此将其保

  • 我试图弄清楚如何处理Passport包中的密码授予令牌。我是否应该将客户id和客户机密存储在中。env文件或在请求令牌时直接从数据库获取值?

  • 我正在构建一个API,并使用Laravel Passport进行身份验证。 API正在用于我们的移动应用程序,因此我们正在使用密码授予客户端。 一切都很好,用户可以登录以获得访问令牌。我们创建了一个注册endpoint,允许用户注册。此时,我们也需要API返回访问令牌。 通过查看文档,无法通过编程方式创建访问令牌。 如何在控制器中为密码授予客户端创建访问令牌?我显然不想对自己的API执行HTTP请

  • 我开发Spring Cloud(使用Netflix OSS堆栈)微服务架构已经有一段时间了。正如您所料,我已经将授权服务器分离为一个独立的微服务。我的前端应用程序使用“密码”授权类型用于用户登录目的。但是,对于从前端服务到其他后端服务的rest调用,我使用的是“Client-Credentials”授权类型。客户机凭据授予类型也在其他后端服务中使用。通过这样做,我无法确定谁是请求的实际调用者(当前

  • 我想使用Spring Security的资源服务器和我的组件中包含的授权服务器来保护我的应用程序。所需的流包括仅使用客户端凭据授予类型并将client_id与client_secret一起传递为bas64标头,在命中终结点后,应该返回令牌以用于进一步的请求。我还包括grant_type:请求参数中的客户端凭据 目前我收到错误:。奇怪的是,尽管我进行了配置,Spring仍然会生成控制台日志中可以看到