如何在 Spring Boot 微服务中使用 OAuth2 授权保护传出 API 调用,同时保持传入 API 调用不受保护?

问题描述 投票:0回答:1

我有两个 Spring Boot 服务,S1 和 S2,它们都有自己的 API 端点。 S1 受 OAuth2 客户端凭据保护,因此我已将 S2 配置为 OAuth2 客户端,并使用

WebClient
在 S2 上发出 API 请求。为此,我向我的 S2 应用程序添加了两个依赖项

  1. spring-boot-starter-oauth2-client
  2. spring-boot-starter-webflux
    .

WebClient
和我的
application.properties
文件的配置如下:

spring:
  security:
    oauth2:
      client:
        registration:
          liferayclient:
            client-id: ${CLIENT_ID:bcvc7yt7}
            client-secret: ${CLIENT_SECRET:vhsvchc76}
            authorization-grant-type: client_credentials
        provider:
          liferayclient:
            token-uri: localhost:8080/o/oauth2/token

WebClient
配置为:

@Configuration
public class WebClientConfig {

    @Bean
    WebClient webClient(AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
        ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder()
                .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(16 * 1024 * 1024))
                .build();

        ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
                new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
        oauth2Client.setDefaultClientRegistrationId("liferayclient");
        HttpClient httpClient = HttpClient.create()
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10 * 1000)
                .doOnConnected(connection -> {
                    connection.addHandlerLast(new ReadTimeoutHandler(2 * 60 * 1000L, MILLISECONDS));
                    connection.addHandlerLast(new WriteTimeoutHandler(2 * 60 * 1000L, MILLISECONDS));
                });

        return WebClient.builder()
                .filter(oauth2Client)
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .exchangeStrategies(exchangeStrategies)
                .build();
    }

    @Bean
    ReactiveClientRegistrationRepository clientRegistrations(
            @Value("${spring.security.oauth2.client.provider.liferayclient.token-uri}") String token_uri,
            @Value("${spring.security.oauth2.client.registration.liferayclient.client-id}") String client_id,
            @Value("${spring.security.oauth2.client.registration.liferayclient.client-secret}") String client_secret,
            @Value("${spring.security.oauth2.client.registration.liferayclient.authorization-grant-type}") String authorizationGrantType

    ) {
        ClientRegistration registration = ClientRegistration
                .withRegistrationId("liferayclient")
                .tokenUri(token_uri)
                .clientId(client_id)
                .clientSecret(client_secret)
                .authorizationGrantType(new AuthorizationGrantType(authorizationGrantType))
                .build();
        return new InMemoryReactiveClientRegistrationRepository(registration);
    }

    @Bean
    public AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager(
            ReactiveClientRegistrationRepository clientRegistrationRepository) {

        InMemoryReactiveOAuth2AuthorizedClientService clientService =
                new InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrationRepository);

        ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
                ReactiveOAuth2AuthorizedClientProviderBuilder.builder().clientCredentials().build();

        AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager =
                new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
                        clientRegistrationRepository, clientService);

        authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

        return authorizedClientManager;
    }
}

现在谈到这个问题,我能够使用 Web 客户端从 S2 成功地对 S1 进行 API 调用。但是,我遇到了无法调用服务 S2 上的任何 API 的问题。每当我尝试在 S2 上进行 API 调用时,我都会在 Web 浏览器上收到“使用 OAuth 2.0 登录”提示。我得到的 HTML 响应如下所示。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="">
    <meta name="author" content="">
    <title>Please sign in</title>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
    <link href="https://getbootstrap.com/docs/4.0/examples/signin/signin.css" rel="stylesheet"
        crossorigin="anonymous" />
</head>

<body>
    <div class="container">
        <h2 class="form-signin-heading">Login with OAuth 2.0</h2>
        <table class="table table-striped">
        </table>
    </div>
</body>

</html>

在添加

spring-boot-starter-oauth2-client
依赖项之前,我能够使用 Postman 毫无问题地进行这些 API 调用。看来,既然添加了依赖,Spring 现在期望当前的微服务也受到 OAuth2 的保护。因此,在当前微服务中创建的任何 REST API 都将自动受到 OAuth2 的保护。

我正在寻求有关如何解决此问题并使拨出电话需要 OAuth2 授权,同时允许拨入电话绕过此要求的建议。我正在使用 Spring Boot 版本 3.0.0,我尝试过的解决方案之一如下。

package com.report.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class SecurityConfig{
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
        return http
            .csrf().disable()
            .authorizeHttpRequests()
                .requestMatchers("*").permitAll()
                .and()
            .build();
    }
}

我试图创建一个允许所有 HTTP 请求的自定义

SecurityFilterChain
bean,但它对我没有帮助。尽管我可以像以前一样使用
WebClient
从 S2 到 S1 进行 API 调用,但我仍然无法通过 Postman 在 S2 上进行任何 API 调用。添加此内容后,我现在收到禁止错误。

spring spring-boot spring-security spring-webclient spring-oauth2
1个回答
0
投票

经过一番调查,我找到了自己问题的解决方案。 在使用 Spring Security 允许访问所有无需认证或授权的请求时,我最初使用了以下配置:

http
    .csrf().disable()
    .authorizeRequests()
        .requestMatchers("*").permitAll()
        .and()
    .build();

但是,这并没有按预期工作,在向我的 Spring Boot 应用程序发出请求时,我仍然收到 401 Unauthorized 错误。

然后我尝试使用以下配置:

http
    .csrf().disable()
    .authorizeRequests()
        .anyRequest().permitAll()
        .and()
    .build();

这按预期工作,允许访问所有请求而无需身份验证或授权。

我不确定为什么在我的情况下使用

requestMatchers("*")
不起作用,但使用
anyRequest()
似乎解决了这个问题。

我希望这可以帮助其他面临类似问题的人。

© www.soinside.com 2019 - 2024. All rights reserved.