You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Quarkus中结合CXF WSS4J与Elytron Security,通过扩展UsernameTokenValidator实现LDAP认证的技术咨询

在Quarkus中结合CXF WSS4J与Elytron Security,通过扩展UsernameTokenValidator实现LDAP认证的技术咨询

看起来你已经找对了核心方向——通过扩展CXF的UsernameTokenValidator对接Quarkus Elytron的LDAP认证,这是个很合理的方案,我来帮你把这个实现补全,同时梳理几个关键的注意点:

首先,先确保你的项目依赖是完整的,pom.xml里需要包含这几个核心依赖:

<dependencies>
    <!-- Quarkus CXF JAX-WS支持 -->
    <dependency>
        <groupId>io.quarkiverse.cxf</groupId>
        <artifactId>cxf-quarkus-jaxws</artifactId>
    </dependency>
    <!-- Quarkus Elytron LDAP安全扩展 -->
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-elytron-security-ldap</artifactId>
    </dependency>
    <!-- CXF WS安全模块 -->
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-ws-security</artifactId>
    </dependency>
</dependencies>

接下来是核心的UsernameTokenLDAPValidator实现,这里要注意绝对不能用new来实例化这个类,因为我们需要注入Quarkus的安全认证API来复用Elytron的LDAP配置,所以要把它声明为CDI Bean,然后在CXF Endpoint配置中注入使用:

import org.apache.cxf.ws.security.tokenstore.SecurityToken;
import org.apache.cxf.ws.security.validate.UsernameTokenValidator;
import org.apache.cxf.ws.security.validate.ValidationResult;
import io.quarkus.security.AuthenticationRequest;
import io.quarkus.security.SecurityAuthenticator;
import io.quarkus.security.identity.SecurityIdentity;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.w3c.dom.Element;

@ApplicationScoped
public class UsernameTokenLDAPValidator extends UsernameTokenValidator {

    @Inject
    SecurityAuthenticator securityAuthenticator;

    @Override
    public ValidationResult validate(SecurityToken token, Element element, String realm) {
        // 先调用父类做基础的格式验证,比如token的结构是否合法
        ValidationResult result = super.validate(token, element, realm);
        
        if (result.getStatus() == ValidationResult.Status.VALID) {
            String username = token.getPrincipal().getName();
            // 从token中获取明文密码(你的场景是明文密码传输,所以直接取即可)
            String plainPassword = (String) token.getCredentials();
            
            try {
                // 复用Quarkus Elytron的认证API执行LDAP验证
                AuthenticationRequest authRequest = AuthenticationRequest
                        .withUsername(username)
                        .password(plainPassword.toCharArray())
                        .build();
                
                // 同步执行认证,你也可以根据业务需求改用异步方式
                SecurityIdentity authenticatedIdentity = securityAuthenticator.authenticate(authRequest)
                        .await()
                        .indefinitely();
                
                // 认证成功后,把Elytron返回的身份信息绑定到CXF的验证结果中
                result.setPrincipal(authenticatedIdentity.getPrincipal());
                result.setCredentials(authenticatedIdentity.getCredentials());
            } catch (Exception e) {
                // 认证失败时设置错误状态和提示信息,CXF会自动返回对应的SOAP错误
                result.setStatus(ValidationResult.Status.INVALID);
                result.setErrorMessage("LDAP用户认证失败:" + e.getMessage());
            }
        }
        
        return result;
    }
}

然后补全你的CXF Endpoint配置,这里要注意注入我们的Validator(而不是直接new),同时完善WSS4J的入站拦截器配置:

import org.apache.cxf.quarkus.core.CxfEndpoint;
import org.apache.cxf.ws.security.SecurityConstants;
import org.apache.cxf.ws.security.wss4j.WSS4JInHandler;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
import jakarta.inject.Inject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@ApplicationScoped
public class CxfServiceProducer {

    @Inject
    UsernameTokenLDAPValidator usernameTokenLDAPValidator;

    @Produces
    public CxfEndpoint myServiceEndpoint() {
        CxfEndpoint cxfEndpoint = new CxfEndpoint();
        cxfEndpoint.setDataFormat(CxfEndpoint.DataFormat.CXF_MESSAGE);
        cxfEndpoint.setWsdlURL("wsdl/Service123.wsdl");
        cxfEndpoint.setAddress("/MyService");

        // 配置CXF安全属性,指定我们的自定义UsernameToken验证器
        Map<String, Object> endpointProps = new HashMap<>();
        endpointProps.put(SecurityConstants.USERNAME_TOKEN_VALIDATOR, usernameTokenLDAPValidator);
        cxfEndpoint.setProperties(endpointProps);

        // 配置WSS4J入站处理器,开启UsernameToken验证逻辑
        Map<String, Object> securityProps = new HashMap<>();
        securityProps.put("action", "UsernameToken");
        // 因为你的场景是明文密码,所以指定密码类型为PasswordText
        securityProps.put("passwordType", "PasswordText");

        List<Map<String, Object>> inInterceptors = new ArrayList<>();
        Map<String, Object> wss4jInHandlerProps = new HashMap<>(securityProps);
        wss4jInHandlerProps.put("handlerClass", WSS4JInHandler.class.getName());
        inInterceptors.add(wss4jInHandlerProps);
        
        cxfEndpoint.setInInterceptors(inInterceptors);

        return cxfEndpoint;
    }
}

最后,在application.properties中配置Elytron的LDAP参数,这里以标准的开源LDAP服务器为例:

# 启用Elytron LDAP安全模块
quarkus.elytron.security.ldap.enabled=true
# LDAP服务器地址,生产环境建议用LDAPS(ldaps://xxx:636)
quarkus.elytron.security.ldap.url=ldap://your-ldap-host:389
# 用户搜索的基础DN,根据你的LDAP目录结构调整
quarkus.elytron.security.ldap.search-base-dn=ou=users,dc=your-domain,dc=com
# 用户搜索过滤规则,{0}会被自动替换为待验证的用户名
quarkus.elytron.security.ldap.search-filter=(uid={0})
# LDAP管理员账号(用于绑定LDAP服务器并搜索用户)
quarkus.elytron.security.ldap.context-source.principal=cn=admin,dc=your-domain,dc=com
# LDAP管理员密码
quarkus.elytron.security.ldap.context-source.credential=your-admin-password
# 用户的用户名属性,对应LDAP中的用户唯一标识字段
quarkus.elytron.security.ldap.principal-name-attribute=uid

几个关键的注意点:

  • 绝对不要手动编写LDAP连接和验证代码,完全复用Quarkus Elytron的实现,这样可以获得Quarkus的配置管理、连接池、错误处理等原生能力
  • 你的场景是明文密码传输,生产环境必须强制使用HTTPS,否则密码会在网络中明文泄露
  • 如果你的LDAP服务器开启了TLS加密,只需要把url改成ldaps://your-ldap-host:636,并根据需要配置信任库参数

备注:内容来源于stack exchange,提问作者coder5749

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 15:00:29