Spring配置文件:
<!-- 发布WebService服务(Spring配置文件中的配置) -->
<jaxws:server id="xxWS" serviceClass="com.xx.xx" address="/xx">
<jaxws:serviceBean>
<bean class="com.xx.xxImpl"></bean>
</jaxws:serviceBean>
<jaxws:inInterceptors>
<bean class="com.xx.LoggingInterceptor"></bean>
<bean class="com.xx.AuthInterceptor"></bean>
</jaxws:inInterceptors>
<jaxws:outInterceptors>
<bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"></bean>
</jaxws:outInterceptors>
</jaxws:server>
自定义拦截器验证用户信息实现
import java.util.List;
import javax.xml.soap.SOAPException;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.headers.Header;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
public class AuthInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
public AuthInterceptor() {
super(Phase.PRE_PROTOCOL);
}
@Override
public void handleMessage(SoapMessage message) throws Fault {
String user = null, passwd = null;
List<Header> headers = message.getHeaders();
if (headers == null) {
SOAPException soapEx = new SOAPException(Constant.ERROR_CODE_5001 + "::Authentication failed!");
throw new Fault(soapEx);
} else {
for (Header header : headers) {
if (header == null)
continue;
Element element = (Element) header.getObject();
if (element == null)
continue;
Node node = element.getFirstChild();
if (node == null)
continue;
if (element.getNodeName().equals("user")) {
user = node.getTextContent();
}
if (element.getNodeName().equals("passwd")) {
passwd = node.getTextContent();
}
}
}
if (user == null || passwd == null) {
SOAPException soapEx = new SOAPException(Constant.ERROR_CODE_5000 + "::Authentication failed!");
throw new Fault(soapEx);
}
// 验证用户名和密码是否正确...
}
}
自定义拦截器记录日志,PS:可以用CXF本身的日志记录拦截器:
org.apache.cxf.interceptor.LoggingInInterceptor
替换配置文件中的
com.xx.LoggingInterceptor
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.io.IOUtils;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.apache.log4j.Logger;
import com.cabinet.eip.utils.StringUtils;
public class LoggingInInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
private Logger logger = Logger.getLogger(LoggingInInterceptor.class);
public LoggingInInterceptor() {
super(Phase.RECEIVE);
}
public void setLoggerName(String loggerName) {
this.logger = Logger.getLogger(loggerName);
}
public void setLogger(Logger logger) {
this.logger = logger;
}
@Override
public void handleMessage(SoapMessage message) {
long logID = System.nanoTime();
InputStream is = null;
try {
// 用来记录当前报文的ID,为了日志里标识单次请求的所有日志
message.getExchange().put("LOG.SEQUENCE", logID);
is = message.getContent(InputStream.class);
String xml = IOUtils.toString(is, "UTF-8");
message.setContent(InputStream.class, new ByteArrayInputStream(xml.getBytes("UTF-8")));
if (xml == null || xml.equals("")) {
return;
}
// StringUtils为自己写的用dom4j格式化xml,不格式化输出的内容是一行,这里不贴了,网上搜很多
xml = StringUtils.formatXML(xml, null);
String httpMethod = (String) message.get(Message.HTTP_REQUEST_METHOD);
StringBuilder info = new StringBuilder();
info.append("------接收到Soap报文(").append(logID).append("), httpMethod:").append(httpMethod).append(xml);
logger.info(info.toString());
} catch (Exception e) {
logger.error("CXF记录日志错误(" + logID + "): ", e);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
}
}
}
}
}
客户端发送过来的格式应如下:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ws="xxx">
<soapenv:Header>
<user>username</user>
<passwd>加密后的密码</passwd>
</soapenv:Header>
<soapenv:Body>
<!-- xx的名字为要调用的WebService方法名 -->
<ws:xx>
<!-- 实际调用的方法所传递的参数 -->
</ws:xx>
</soapenv:Body>
</soapenv:Envelope>
补充: 自定义LoggingOutInterceptor
这部分纠结了很久,参考自 http://stackoverflow.com/questions/6438178/cxf-outgoing-interceptor-get-soap-response-body-that-is-always-null,
其实就是重现了源代码的方式。
org.apache.cxf.interceptor.LoggingOutInterceptor
代码如下:
import java.io.OutputStream;
import org.apache.commons.io.IOUtils;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.io.CacheAndWriteOutputStream;
import org.apache.cxf.io.CachedOutputStream;
import org.apache.cxf.io.CachedOutputStreamCallback;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.apache.log4j.Logger;
public class LoggingOutInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
private Logger logger = Logger.getLogger(LoggingOutInterceptor.class);
public LoggingOutInterceptor() {
super(Phase.PRE_STREAM);
}
@Override
public void handleMessage(SoapMessage message) {
String logID = "";
try {
// 获取之前声明的日志ID标识,单次请求输出的所有日志标识相同
Object obj = message.getExchange().get("LOG.SEQUENCE");
if (obj != null)
logID = obj.toString();
OutputStream os = message.getContent(OutputStream.class);
CacheAndWriteOutputStream cwos = new CacheAndWriteOutputStream(os);
message.setContent(OutputStream.class, cwos);
cwos.registerCallback(new LoggingOutCallBack(logID));
} catch (Exception e) {
logger.error("CXF记录日志错误(" + logID + "): ", e);
}
}
class LoggingOutCallBack implements CachedOutputStreamCallback {
private String logID;
public LoggingOutCallBack(String logID) {
this.logID = logID;
}
@Override
public void onClose(CachedOutputStream cos) {
try {
if (cos != null) {
String xml = IOUtils.toString(cos.getInputStream());
if (xml == null || xml.equals(""))
return;
logger.info("------发出的Soap报文(" + logID + ")" + xml);
}
} catch (Exception e) {
logger.error("CXF记录日志错误(" + logID + "): ", e);
}
try {
cos.lockOutputStream();
cos.resetOut(null, false);
} catch (IOException e) {
}
}
@Override
public void onFlush(CachedOutputStream arg0) {
}
}
}