第 10 章 Quarkus CXF 安全指南


本章提供有关使用 Quarkus CXF 扩展时的安全性的信息。

10.1. 安全指南

安全指南记录了 Quarkus CXF 的各种与安全方面:

10.1.1. SSL、TLS 和 HTTPS

本节记录了与 SSL、TLS 和 HTTPS 相关的各种用例。

注意

本节中使用的代码片段示例来自 Quarkus CXF 的源树中的 WS-SecurityPolicy 集成测试

10.1.1.1. 客户端 SSL 配置

如果您的客户端要与客户端的操作系统不受信任的 SSL 证书的服务器通信,则需要为您的客户端设置自定义信任存储。

openssl 或 Java keytool 等工具通常用于创建和维护信任存储。

我们在两个工具在 Quarkus CXF 源树中都有示例:

准备好信任存储后,您需要将客户端配置为使用它。

这是设置客户端信任存储的最简单方法。关键角色由以下属性扮演:

下面是一个示例:

application.properties

# Client side SSL
quarkus.cxf.client.hello.client-endpoint-url = https://localhost:${quarkus.http.test-ssl-port}/services/hello
quarkus.cxf.client.hello.service-interface = io.quarkiverse.cxf.it.security.policy.HelloService
1

quarkus.cxf.client.hello.trust-store-type = pkcs12
2

quarkus.cxf.client.hello.trust-store = client-truststore.pkcs12
quarkus.cxf.client.hello.trust-store-password = client-truststore-password
Copy to Clipboard Toggle word wrap

1
pkcs12jks 是两种常用的密钥存储格式。PKCS12 是默认的 Java 密钥存储格式,自 Java 9 起。我们建议使用 PKCS12 而不是 JKS,因为它提供更强大的加密算法,因此可扩展、标准化、语言中和广泛支持。
2
引用的 client-truststore.pkcs12 文件必须在 classpath 或文件系统中可用。

10.1.1.2. 服务器 SSL 配置

要通过 HTTPS 协议将您的服务可用,您需要在第一个位置设置服务器密钥存储。服务器 SSL 配置由 Vert.x ( Quarkus 的 HTTP 层)驱动。Quarkus HTTP 指南 提供有关配置选项的信息。

下面是一个基本示例:

application.properties

# Server side SSL
quarkus.tls.key-store.p12.path = localhost-keystore.pkcs12
quarkus.tls.key-store.p12.password = localhost-keystore-password
quarkus.tls.key-store.p12.alias = localhost
quarkus.tls.key-store.p12.alias-password = localhost-keystore-password
Copy to Clipboard Toggle word wrap

10.1.1.3. Mutual TLS (mTLS)身份验证

到目前为止,我们解释了一个简单的或单端情况,其中只有服务器通过 SSL 证书证明其身份,客户端必须设置为信任该证书。通过让客户端使用相同的公钥加密方式证明其身份,从而进行双向 TLS 身份验证。

因此,对于 Mutual TLS (mTLS)身份验证,除了设置服务器密钥存储和客户端信任存储外,还需要在客户端上设置密钥存储和服务器端的信任存储。

创建和维护存储的工具相同,要使用的配置属性与简单 TLS 案例中使用的配置属性非常相似。

Quarkus CXF 源树中的 mTLS 集成测试 可以作为良好起点。

keystores 和 truststores 使用 openssl (或使用 Java keytool)创建。

以下是 application.properties 文件:

application.properties

# Server keystore for Simple TLS
quarkus.tls.localhost-pkcs12.key-store.p12.path = localhost-keystore.pkcs12
quarkus.tls.localhost-pkcs12.key-store.p12.password = localhost-keystore-password
quarkus.tls.localhost-pkcs12.key-store.p12.alias = localhost
quarkus.tls.localhost-pkcs12.key-store.p12.alias-password = localhost-keystore-password
# Server truststore for Mutual TLS
quarkus.tls.localhost-pkcs12.trust-store.p12.path = localhost-truststore.pkcs12
quarkus.tls.localhost-pkcs12.trust-store.p12.password = localhost-truststore-password
# Select localhost-pkcs12 as the TLS configuration for the HTTP server
quarkus.http.tls-configuration-name = localhost-pkcs12

# Do not allow any clients which do not prove their indentity through an SSL certificate
quarkus.http.ssl.client-auth = required

# CXF service
quarkus.cxf.endpoint."/mTls".implementor = io.quarkiverse.cxf.it.auth.mtls.MTlsHelloServiceImpl

# CXF client with a properly set certificate for mTLS
quarkus.cxf.client.mTls.client-endpoint-url = https://localhost:${quarkus.http.test-ssl-port}/services/mTls
quarkus.cxf.client.mTls.service-interface = io.quarkiverse.cxf.it.security.policy.HelloService
quarkus.cxf.client.mTls.key-store = target/classes/client-keystore.pkcs12
quarkus.cxf.client.mTls.key-store-type = pkcs12
quarkus.cxf.client.mTls.key-store-password = client-keystore-password
quarkus.cxf.client.mTls.key-password = client-keystore-password
quarkus.cxf.client.mTls.trust-store = target/classes/client-truststore.pkcs12
quarkus.cxf.client.mTls.trust-store-type = pkcs12
quarkus.cxf.client.mTls.trust-store-password = client-truststore-password

# Include the keystores in the native executable
quarkus.native.resources.includes = *.pkcs12,*.jks
Copy to Clipboard Toggle word wrap

10.1.1.4. 通过 WS-SecurityPolicy 强制 SSL

客户端通过 HTTPS 连接的要求可以在策略中定义。

功能由 quarkus-cxf-rt-ws-security 扩展提供。

以下是策略文件的示例:

https-policy.xml

<?xml version="1.0" encoding="UTF-8"?>
<wsp:Policy wsp:Id="HttpsSecurityServicePolicy"
            xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
    xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"
    xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <wsp:ExactlyOne>
        <wsp:All>
            <sp:TransportBinding>
                <wsp:Policy>
                    <sp:TransportToken>
                        <wsp:Policy>
                            <sp:HttpsToken RequireClientCertificate="false" />
                        </wsp:Policy>
                    </sp:TransportToken>
                    <sp:IncludeTimestamp />
                    <sp:AlgorithmSuite>
                        <wsp:Policy>
                            <sp:Basic128 />
                        </wsp:Policy>
                    </sp:AlgorithmSuite>
                </wsp:Policy>
            </sp:TransportBinding>
        </wsp:All>
    </wsp:ExactlyOne>
</wsp:Policy>
Copy to Clipboard Toggle word wrap

该策略必须从服务端点接口(SEI)引用:

HttpsPolicyHelloService.java

package io.quarkiverse.cxf.it.security.policy;

import jakarta.jws.WebMethod;
import jakarta.jws.WebService;

import org.apache.cxf.annotations.Policy;

/**
 * A service implementation with a transport policy set
 */
@WebService(serviceName = "HttpsPolicyHelloService")
@Policy(placement = Policy.Placement.BINDING, uri = "https-policy.xml")
public interface HttpsPolicyHelloService extends AbstractHelloService {

    @WebMethod
    @Override
    public String hello(String text);

}
Copy to Clipboard Toggle word wrap

在这个版本中,通过 HTTP 发送的任何请求都会被 PolicyVerificationInInterceptor 拒绝:

ERROR [org.apa.cxf.ws.pol.PolicyVerificationInInterceptor] Inbound policy verification failed: These policy alternatives can not be satisfied:
 {http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702}TransportBinding: TLS is not enabled
 ...
Copy to Clipboard Toggle word wrap

10.1.2. 认证和授权

注意

本节中显示的代码片段示例来自 Quarkus CXF 的源树中的客户端和服务器 集成测试。您可能需要将其用作可运行的示例。

10.1.2.1. 客户端 HTTP 基本身份验证

使用以下由 quarkus-cxf 扩展提供的客户端配置选项,为 HTTP 基本身份验证传递用户名和密码:

下面是一个示例:

application.properties

quarkus.cxf.client.basicAuth.wsdl = http://localhost:${quarkus.http.test-port}/soap/basicAuth?wsdl
quarkus.cxf.client.basicAuth.client-endpoint-url = http://localhost:${quarkus.http.test-port}/soap/basicAuth
quarkus.cxf.client.basicAuth.username = bob
quarkus.cxf.client.basicAuth.password = bob234
Copy to Clipboard Toggle word wrap

10.1.2.1.1. 访问由基本身份验证保护的 WSDL

默认情况下,由 Quarkus CXF 创建的客户端不会发送 Authorization 标头,除非您将 quarkus.cxf.client."client-name".secure-wsdl-access 设置为 true

application.properties

quarkus.cxf.client.basicAuthSecureWsdl.wsdl = http://localhost:${quarkus.http.test-port}/soap/basicAuth?wsdl
quarkus.cxf.client.basicAuthSecureWsdl.client-endpoint-url = http://localhost:${quarkus.http.test-port}/soap/basicAuthSecureWsdl
quarkus.cxf.client.basicAuthSecureWsdl.username = bob
quarkus.cxf.client.basicAuthSecureWsdl.password = ${client-server.bob.password}
quarkus.cxf.client.basicAuthSecureWsdl.secure-wsdl-access = true
Copy to Clipboard Toggle word wrap

10.1.2.2. Mutual TLS (mTLS)身份验证

请参阅 SSL、TLS 和 HTTPS 指南中的 Mutual TLS (mTLS)身份验证 部分。

10.1.2.3. 保护服务端点

服务器端身份验证和授权由 Quarkus 安全 驱动,特别是在出现时

我们的 客户端和服务器集成测试 中有一个基本示例。其关键部分是:

  • io.quarkus:quarkus-elytron-security-properties-file 依赖项作为身份提供程序
  • 启用基本身份验证,并在 application.properties 中配置其角色:

    application.properties

    quarkus.http.auth.basic = true
    quarkus.security.users.embedded.enabled = true
    quarkus.security.users.embedded.plain-text = true
    quarkus.security.users.embedded.users.alice = alice123
    quarkus.security.users.embedded.roles.alice = admin
    quarkus.security.users.embedded.users.bob = bob234
    quarkus.security.users.embedded.roles.bob = app-user
    Copy to Clipboard Toggle word wrap

  • 基于角色的访问控制通过 @RolesAllowed 注释提供:

BasicAuthHelloServiceImpl.java

package io.quarkiverse.cxf.it.auth.basic;

import jakarta.annotation.security.RolesAllowed;
import jakarta.jws.WebService;

import io.quarkiverse.cxf.it.HelloService;

@WebService(serviceName = "HelloService", targetNamespace = HelloService.NS)
@RolesAllowed("app-user")
public class BasicAuthHelloServiceImpl implements HelloService {
    @Override
    public String hello(String person) {
        return "Hello " + person + "!";
    }
}
Copy to Clipboard Toggle word wrap

10.1.3. WS-SecurityPolicy 强制的身份验证

您可以通过 WS-SecurityPolicy 强制身份验证,而不是为客户端 和服务 进行 Mutual TLS 和基本身份验证。???

要通过 WS-SecurityPolicy 强制身份验证,请按照以下步骤操作:

  1. 将支持的令牌策略添加到 WSDL 合同中的端点。
  2. 在服务器端,实施身份验证回调处理程序,并将其与 application.properties 或环境变量中的端点关联。从客户端接收的凭证由回调处理程序进行身份验证。
  3. 在客户端,通过 application.properties 或环境变量中的配置来提供凭证。或者,您可以实施身份验证回调处理程序来传递凭证。

10.1.3.1. 指定身份验证策略

如果要在服务端点中强制实施身份验证,请将 支持的令牌 策略断言与相关端点绑定相关联,并在其下指定一个或多个 令牌断言

有几种不同类型的支持令牌策略断言,其 XML 元素以 SupportingTokens 结尾(例如: SupportingTokensSignedSupportingTokens 等)。有关完整的列表,请参阅 WS-SecurityPolicy 规范的 Supporting Tokens 部分。

10.1.3.2. UsernameToken 策略断言示例

提示

本节中使用的代码片段示例来自 Quarkus CXF 的源树中 WS-SecurityPolicy 集成测试。您可能需要将其用作可运行的示例。

下表显示了一个策略示例,它要求包含在安全标头中包含 WS-Security UsernameToken (包含用户名/密码凭据)。

username-token-policy.xml

<?xml version="1.0" encoding="UTF-8"?>
<wsp:Policy
        wsp:Id="UsernameTokenSecurityServicePolicy"
        xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
    xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"
    xmlns:sp13="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200802"
    xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <wsp:ExactlyOne>
        <wsp:All>
            <sp:SupportingTokens>
                <wsp:Policy>
                    <sp:UsernameToken
                        sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
                        <wsp:Policy>
                            <sp:WssUsernameToken11 />
                            <sp13:Created />
                            <sp13:Nonce />
                        </wsp:Policy>
                    </sp:UsernameToken>
                </wsp:Policy>
            </sp:SupportingTokens>
        </wsp:All>
    </wsp:ExactlyOne>
</wsp:Policy>
Copy to Clipboard Toggle word wrap

您可以通过两种方式将此策略文件与服务端点关联:

  • 引用服务端点接口(SEI)上的策略,如下所示:

    UsernameTokenPolicyHelloService.java

    @WebService(serviceName = "UsernameTokenPolicyHelloService")
    @Policy(placement = Policy.Placement.BINDING, uri = "username-token-policy.xml")
    public interface UsernameTokenPolicyHelloService extends AbstractHelloService {
        ...
    }
    Copy to Clipboard Toggle word wrap

  • 在您的 WSDL 合同中包含 策略,并通过 PolicyReference 元素引用该策略

当您有策略就位时,在服务端点和客户端上配置凭证:

application.properties

# A service with a UsernameToken policy assertion
quarkus.cxf.endpoint."/helloUsernameToken".implementor = io.quarkiverse.cxf.it.security.policy.UsernameTokenPolicyHelloServiceImpl
quarkus.cxf.endpoint."/helloUsernameToken".security.callback-handler = #usernameTokenPasswordCallback

# These properties are used in UsernameTokenPasswordCallback
# and in the configuration of the helloUsernameToken below
wss.user = cxf-user
wss.password = secret

# A client with a UsernameToken policy assertion
quarkus.cxf.client.helloUsernameToken.client-endpoint-url = https://localhost:${quarkus.http.test-ssl-port}/services/helloUsernameToken
quarkus.cxf.client.helloUsernameToken.service-interface = io.quarkiverse.cxf.it.security.policy.UsernameTokenPolicyHelloService
quarkus.cxf.client.helloUsernameToken.security.username = ${wss.user}
quarkus.cxf.client.helloUsernameToken.security.password = ${wss.password}
Copy to Clipboard Toggle word wrap

在上面的列表中,usernameTokenPasswordCallback 是实施 javax.security.auth.callback.CallbackHandler@jakarta.inject.Named bean 的名称。Quarkus CXF 将在 CDI 容器中 使用此名称 查找 bean。

以下是 bean 的示例实施:

UsernameTokenPasswordCallback.java

package io.quarkiverse.cxf.it.security.policy;

import java.io.IOException;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Named;

import org.apache.wss4j.common.ext.WSPasswordCallback;
import org.eclipse.microprofile.config.inject.ConfigProperty;

@ApplicationScoped
@Named("usernameTokenPasswordCallback") /* We refer to this bean by this name from application.properties */
public class UsernameTokenPasswordCallback implements CallbackHandler {

    /* These two configuration properties are set in application.properties */
    @ConfigProperty(name = "wss.password")
    String password;
    @ConfigProperty(name = "wss.user")
    String user;

    @Override
    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        if (callbacks.length < 1) {
            throw new IllegalStateException("Expected a " + WSPasswordCallback.class.getName()
                    + " at possition 0 of callbacks. Got array of length " + callbacks.length);
        }
        if (!(callbacks[0] instanceof WSPasswordCallback)) {
            throw new IllegalStateException(
                    "Expected a " + WSPasswordCallback.class.getName() + " at possition 0 of callbacks. Got an instance of "
                            + callbacks[0].getClass().getName() + " at possition 0");
        }
        final WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
        if (user.equals(pc.getIdentifier())) {
            pc.setPassword(password);
        } else {
            throw new IllegalStateException("Unexpected user " + user);
        }
    }

}
Copy to Clipboard Toggle word wrap

要测试整个设置,您可以创建一个简单的 @QuarkusTest

UsernameTokenTest.java

package io.quarkiverse.cxf.it.security.policy;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

import io.quarkiverse.cxf.annotation.CXFClient;
import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest
public class UsernameTokenTest {

    @CXFClient("helloUsernameToken")
    UsernameTokenPolicyHelloService helloUsernameToken;

    @Test
    void helloUsernameToken() {
        Assertions.assertThat(helloUsernameToken.hello("CXF")).isEqualTo("Hello CXF from UsernameToken!");
    }
}
Copy to Clipboard Toggle word wrap

通过 mvn test -Dtest=UsernameTokenTest 运行测试时,您应该会看到一个 SOAP 消息,其中包含 UsernamePassword

UsernameTokenTest 的日志输出

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soap:mustUnderstand="1">
      <wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="UsernameToken-bac4f255-147e-42a4-aeec-e0a3f5cd3587">
        <wsse:Username>cxf-user</wsse:Username>
        <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">secret</wsse:Password>
        <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">3uX15dZT08jRWFWxyWmfhg==</wsse:Nonce>
        <wsu:Created>2024-10-02T17:32:10.497Z</wsu:Created>
      </wsse:UsernameToken>
    </wsse:Security>
  </soap:Header>
  <soap:Body>
    <ns2:hello xmlns:ns2="http://policy.security.it.cxf.quarkiverse.io/">
      <arg0>CXF</arg0>
    </ns2:hello>
  </soap:Body>
</soap:Envelope>
Copy to Clipboard Toggle word wrap

10.1.3.3. SAML v1 和 v2 策略断言示例

WS-SecurityPolicy 集成测试 还包含与 SAML v1 和 SAML v2 断言示例类似。

返回顶部
Red Hat logoGithubredditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。 了解我们当前的更新.

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

Theme

© 2025 Red Hat