首页 > 解决方案 > 使用 WSS4J (SOAP) 在 CXF WebServices 中实现身份验证

问题描述

我正在尝试在我的网络服务中使用用户名/密码来实现身份验证。目前我学习了 CXF 以及如何配置东西,但是对于我的生活,我只是无法将我的头包裹在安全部分上。

我使用 CXF 从 WSDL 创建 Java 类。这是我的 Web 服务 XML 配置。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

<jaxws:endpoint id="pacService" implementor="com.logicalprovisioning.internal.PacWS" address="/PacRequestSoapHttpPort">
    <jaxws:properties>
        <entry key="exceptionMessageCauseEnabled" value="true" />
        <entry key="security.callback-handler" value="com.logicalprovisioning.webservices.clients.autheticate.AuthenticationCallback"/>
    </jaxws:properties>
</jaxws:endpoint>
</beans>

我作为验证用户名密码的一部分添加的 AuthenticationCallback 条目。

我的 AutheitcationCallback 目前看起来像这样:

import java.io.IOException;

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

import org.apache.wss4j.common.ext.WSPasswordCallback;

public class AuthenticationCallback {

    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {

        WSPasswordCallback wsPasswordCallback = (WSPasswordCallback) callbacks[0];

        if (wsPasswordCallback.getIdentifier().equals("TIM")) {
            wsPasswordCallback.setPassword("my_password");
        }
    }
}

现在是一个粗略的草案。

我编写了一个客户端,用于初始化 WebService(一个抽象类),并继承它以用于不同的 Web 服务。

public abstract class WebServiceClientBase implements HandlerResolver, SOAPHandler<SOAPMessageContext> {

    protected int connectTimeout;

    protected String endPointUrl;
    protected String password;
    protected String userName;

    protected Logger logger = null;

    private Service service = null;


    public WebServiceClientBase () {
        super();
    }

    public void init() {

        // Set the security handler
        getService().setHandlerResolver(new HandlerResolverImpl());

        // Setting endpoint URL and timeout...
        getBindingProvider().getRequestContext().put("javax.xml.ws.client.receiveTimeout", connectTimeout);
        getBindingProvider().getRequestContext().put("javax.xml.ws.client.connectionTimeout", connectTimeout);
        getBindingProvider().getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endPointUrl);


        Map<String, List<String>> headers = new HashMap<String, List<String>>();

        headers.put("Content-Type", Collections.singletonList("application/soap+xml"));
        headers.put(Message.ENCODING, Collections.singletonList("UTF-8"));

        getBindingProvider().getRequestContext().put(MessageContext.HTTP_REQUEST_HEADERS, headers);
    }

    @Override
    @SuppressWarnings("rawtypes")
    public List<Handler> getHandlerChain(PortInfo arg0) {

        List<Handler> handlerChain = new ArrayList<Handler>();

        handlerChain.add(this);

        return handlerChain;
    }

    @Override
    public boolean handleMessage(SOAPMessageContext soapMessageContext) {

        if (!((Boolean) soapMessageContext.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY)). booleanValue()) {
            return true;
        }

        return true;
    }
    
    @Override
    public boolean handleFault(SOAPMessageContext arg0) {
        return false;
    }

    @Override
    public void close(MessageContext arg0) {

    }

    @Override
    public Set<QName> getHeaders() {
        return null;
    }

    protected Service getService() {
        return service;
    }

    protected void setService(Service service) {
        this.service = service;
    }

    protected abstract BindingProvider getBindingProvider();
}

这就是我已经走了多远。我不确定如何将身份验证信息与 Web 服务调用连接起来。

任何指针都会有所帮助。我也是在正确的轨道上开始吗?通过查看所有教程和示例,我可能会陷入死胡同。

标签: javaweb-servicessoapcxf

解决方案


假设 com.logicalprovisioning.internal.PacWS 是你的 jaxws 生成的 PortType 类,你可以做这样的事情......

...
com.logicalprovisioning.internal.PacWS pacWsPort = getService().get...Port();
org.apache.cxf.endpoint.Client client = org.apache.cxf.frontend.ClientProxy.getClient(pacWSPort);
org.apache.cxf.endpoint.Endpoint endpoint = client.getEndpoint();

Map<String, Object> props = new HashMap<>();
props.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
props.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
props.put(WSHandlerConstants.PW_CALLBACK_CLASS, AuthenticationCallback.class.getName());
props.put(WSHandlerConstants.USER, "TIM");
WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(props);
endpoint.getOutInterceptors().add(wssOut);

// you can then call @WebMethod annotated methods on your PacWS object, and it should populate the wss-security soap header

上面的部分代码大概可以用cxf xml配置文件中的配置代替


推荐阅读