如何使用 Spring boot WebClient 管理 HTTPS 相互身份验证(包括 Bearer Token)?

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

我的帖子的目的是直接分享我对以下主题的回答。我还分享了对我有帮助的链接 => 我正在开发一个基于 Spring webflux 的后端。前端角度应用程序连接到我的后端。 我的后端还通过基于相互身份验证的 HTTPS 连接到业务服务器。 我必须在发送给业务服务器的每个标头请求中设置不记名令牌。 我通过 OAuth 2.0 客户端凭据授权流(通过 HTTPS)从服务器检索此令牌。我下面的回答展示了我如何管理实施。

spring-boot https spring-webflux bearer-token mutual-authentication
1个回答
0
投票

感谢以下链接:

我找到了以下解决方案:

@Configuration
@EnableWebFluxSecurity
public class Oauth2ClientConfig {

    // Bearer Token management based on OAuth 2.0 client credentials grant flow
    @Bean
    ReactiveClientRegistrationRepository getRegistration() {
        ClientRegistration registration = ClientRegistration
                .withRegistrationId("credentialProvider")
                .tokenUri("https://rhsso:8446/auth/token")
                .clientId("client_id").clientSecret("client_secret")
                .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
                .build();
        return new InMemoryReactiveClientRegistrationRepository(registration);
    }
    // Bearer Token management using HTTPS
    @Bean
    public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
            ReactiveClientRegistrationRepository clientRegistrationRepository,
            ReactiveOAuth2AuthorizedClientService authorizedClientService) throws IOException, KeyStoreException,
            NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException {

        WebClientReactiveClientCredentialsTokenResponseClient accessTokenResponseClient = new WebClientReactiveClientCredentialsTokenResponseClient();
              
        final FileInputStream keyStoreFile = new FileInputStream("C:\\keystore\\client_keystore.p12"); // client private key and client certificate
        final FileInputStream trustStoreFile = new FileInputStream("C:\\truststore\\server_truststore.p12");
        final KeyStore keyStore = KeyStore.getInstance("PKCS12");
        final KeyStore trustStore = KeyStore.getInstance("PKCS12");
        final KeyManagerFactory keyManagerFactory = KeyManagerFactory
                .getInstance(KeyManagerFactory.getDefaultAlgorithm());
        final TrustManagerFactory trustManagerFactory = TrustManagerFactory
                .getInstance(TrustManagerFactory.getDefaultAlgorithm());

        String keyStorePassword = "keyStorePassword";
        String trustStorePassword = "trustStorePassword";
        keyStore.load(keyStoreFile, keyStorePassword.toCharArray());
        keyManagerFactory.init(keyStore, keyStorePassword.toCharArray());
        trustStore.load(trustStoreFile, trustStorePassword.toCharArray());
        trustManagerFactory.init(trustStore);
        
        // HTTPS management through sslContext
        SslContextBuilder sslContextBuilder = SslContextBuilder.forClient().keyManager(keyManagerFactory)
                .trustManager(trustManagerFactory);

        SslContext sslContext = sslContextBuilder.build();

        HttpClient httpSslClient = HttpClient.create().secure(ssl -> ssl.sslContext(sslContext));

        ClientHttpConnector httpConnector = new ReactorClientHttpConnector(httpSslClient);

        accessTokenResponseClient.setWebClient(WebClient.builder().clientConnector(httpConnector).build()); 

        ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder
                .builder().clientCredentials(c -> {
                    c.accessTokenResponseClient(accessTokenResponseClient);
                }).build();

        
        AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager = new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
                clientRegistrationRepository, authorizedClientService);
        authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

        return authorizedClientManager;
    }

    @Bean("CustomWebClient") //Qualifier to be used by my dedicated ClientAPI service in order to use the right one among available WebClient beans
    public WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) throws KeyStoreException,
            NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableKeyException {
        ServerOAuth2AuthorizedClientExchangeFilterFunction oauthFunction = new ServerOAuth2AuthorizedClientExchangeFilterFunction(
                authorizedClientManager);
        oauthFunction.setDefaultClientRegistrationId("credentialProvider"); // Link to Bearer token managed above through Its ID

        final FileInputStream keyStoreFile = new FileInputStream("C:\\keystore\\business_client_keystore.p12"); // client private key and client certificate
        final FileInputStream trustStoreFile = new FileInputStream("C:\\truststore\\business_server_truststore.p12");
        final KeyStore keyStore = KeyStore.getInstance("PKCS12");
        final KeyStore trustStore = KeyStore.getInstance("PKCS12");
        final KeyManagerFactory keyManagerFactory = KeyManagerFactory
                .getInstance(KeyManagerFactory.getDefaultAlgorithm());
        final TrustManagerFactory trustManagerFactory = TrustManagerFactory
                .getInstance(TrustManagerFactory.getDefaultAlgorithm());

        String keyStorePassword = "business_keyStorePassword";
        String trustStorePassword = "business_trustStorePassword";
        keyStore.load(keyStoreFile, keyStorePassword.toCharArray());
        keyManagerFactory.init(keyStore, keyStorePassword.toCharArray());
        trustStore.load(trustStoreFile, trustStorePassword.toCharArray());

        trustManagerFactory.init(trustStore);
        // Create Netty SslContext, HttpClient
        SslContextBuilder sslContextBuilder = SslContextBuilder.forClient().keyManager(keyManagerFactory)
                .trustManager(trustManagerFactory);

        SslContext sslContext = sslContextBuilder.build();

        HttpClient httpSslClient = HttpClient.create().secure(ssl -> ssl.sslContext(sslContext));

        // Create Reactive ClientHttpConnector & WebClient
        final ClientHttpConnector httpConnector = new ReactorClientHttpConnector(httpSslClient);

        return WebClient.builder().clientConnector(httpConnector).filter(oauthFunction).build();

    }

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        // disable csrf with frontend interface but generally not recommended otherwise only in special cases
        // cors management in order to accept FrontEnd application installed localhost and only POST method is allowed 
        http.csrf().disable().cors(corsCustomizer -> {
            CorsConfigurationSource configurationSource = request->{
                CorsConfiguration corsConfiguration = new CorsConfiguration();
                corsConfiguration.setAllowedOrigins(List.of("localhost:8080"));
                corsConfiguration.setAllowedMethods(List.of("POST"));
                return corsConfiguration;
            };
            corsCustomizer.configurationSource(configurationSource);
        }).oauth2Client(); 
        
        return http.build();
    }

}

我的网络客户端基于我上面的bean:

@Service
public class ClientAPI {
    

    private WebClient webClient;
    private String businessServiceUrl;

    @Autowired
    public ClientAPI(@Qualifier("CustomWebClient") WebClient webClient) {
        this.businessServiceUrl = "https://hostServerName:8081/theService";
        this.webClient = webClient;
    }

    public Mono<Response> sendRequest(Request bodyRequest) {

        Mono<Response> result = this.webClient.post().uri(this.businessServiceUrl)
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .body(Mono.just(bodyRequest), Request.class).retrieve().bodyToMono(Response.class);

        return result;
    }

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