标签:webservice cxf 安全
项目中需要用到CXF做WS处理,花点时间对其有个简单认识,主要是在安全认证以及日志记录和异常处理这块有要求控制。
安全认证采用的是WSS4J,日志记录和异常处理采用拦截器控制,
资源下载:客户端和服务端都点这
至于webservice的配置可以参考其他文档,
package com.cxfdemo.ws.service;
import java.util.List;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import com.cxfdemo.ws.service.model.Resume;
import com.cxfdemo.ws.service.model.User;
@WebService
public interface HelloWorld {
@WebResult(name = "String")
public String sayHi(@WebParam(name="text")String text);
@WebResult(name = "user")
public User getUser(@WebParam(name="id")String id);
public List<User> getAllUsers();
public void saveUser(@WebParam(name="id")String id,
@WebParam(name="name")String name,
@WebParam(name="sex")int sex);
/**
* 客户端的ObjectFactory的createUser方法的参数必须和User的构造函数的参数一致
* <p>
* 如客户端中需要用到new User("id","name",1)构造User对象时
*
* 需要在ObjectFactory中加入
* public User createUser(String id,String name,int sex) {
* return new User(id,name,sex);
* }
* </p>
* @param user
*/
public void saveUsers(@WebParam(name="user")User user);
@WebResult(name = "String")
public String saveResumes(@WebParam(name="resume")Resume resume);
}
package com.cxfdemo.ws.service;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import javax.activation.DataHandler;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlMimeType;
import com.cxfdemo.ws.service.model.Resume;
import com.cxfdemo.ws.service.model.User;
@WebService(endpointInterface="com.cxfdemo.ws.service.HelloWorld",
serviceName="HelloWorldServiceService",portName="HelloWorldServicePort",
name="HelloWorldService",targetNamespace="http://service.ws.cxfdemo.com/")
public class HelloWorldService implements HelloWorld{
public String sayHi(String text) {
return "Hello " + text;
}
public User getUser(String id) {
return new User("大王", id, 1);
}
public List<User> getAllUsers() {
List<User> list = new ArrayList<User>();
for (int i=0;i<4;i++) {
list.add(new User("小明"+i,""+i,i));
}
return list;
}
public void saveUser(String id, String name, int sex) {
System.out.println(new User(id, name, sex));
}
public void saveUsers(User user) {
System.out.println(user);
}
public String saveResumes(@XmlMimeType("application/octet-stream")Resume resume) {
if (resume == null) {
throw new NullPointerException("参数非法.");
}
DataHandler handler = resume.getDataHandler();
if (handler == null) {
throw new NullPointerException("参数非法.");
}
OutputStream os = null;
InputStream is = null;
try {
is = handler.getInputStream();
os = new FileOutputStream(new File("D:\\"
+ resume.getCandidateName() + "."
+ resume.getResumeFileType()));
byte[] b = new byte[100000];
int bytesRead = 0;
while ((bytesRead = is.read(b)) != -1) {
os.write(b, 0, bytesRead);
}
os.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (os != null) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return "ok";
}
}
有2点是必须配置的。
@XmlMimeType("application/octet-stream")
private DataHandler dataHandler;
<!-- 文件传送必须协议 -->
<jaxws:properties>
<entry key="mtom-enabled" value="true"/>
</jaxws:properties>
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<!-- 设置密码bean -->
<bean id="serverPasswordCallback" class="com.cxfdemo.ws.service.ServerPasswordCallback"></bean>
<!-- WSS4J密码校验 -->
<bean id="wss4jInInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<constructor-arg>
<map>
<!-- 用户认证(明文密码) -->
<entry key="action" value="UsernameToken" />
<entry key="passwordType" value="PasswordText" /><!-- 明文 密文采用PasswordDigest-->
<entry key="passwordCallbackRef" value-ref="serverPasswordCallback" />
</map>
</constructor-arg>
</bean>
<!-- 发布服务 -->
<jaxws:endpoint id="helloWorld" address="/helloWorld"
implementor="com.cxfdemo.ws.service.HelloWorldService">
<!-- 文件传送必须协议 -->
<jaxws:properties>
<entry key="mtom-enabled" value="true"/>
</jaxws:properties>
<!-- 输入拦截器 -->
<jaxws:inInterceptors>
<ref bean="wss4jInInterceptor" />
<!-- 日志打印 -->
<bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
</jaxws:inInterceptors>
<!-- 正常输出拦截器 -->
<jaxws:outInterceptors>
<bean class="com.cxfdemo.ws.service.interceptor.ErrorHandlerInterceptor"></bean>
</jaxws:outInterceptors>
<!-- 错误输出拦截器 -->
<jaxws:outFaultInterceptors>
<bean class="com.cxfdemo.ws.service.interceptor.ErrorHandlerInterceptor"></bean>
</jaxws:outFaultInterceptors>
</jaxws:endpoint>
<jaxws:endpoint id="updateFile" address="/updateFile"
implementor="com.cxfdemo.ws.service.UpdateFileService">
<jaxws:properties>
<entry key="mtom-enabled" value="true" />
</jaxws:properties>
</jaxws:endpoint>
<!-- 全局配置 -->
<!-- <cxf:bus>
<cxf:features>
<cxf:logging />
</cxf:features>
</cxf:bus> -->public class ServerPasswordCallback implements CallbackHandler {
private static final Map<String, String> userMap = new HashMap<String, String>();
static {
userMap.put("client", "clientpass");
userMap.put("server", "serverpass");
}
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
WSPasswordCallback callback = (WSPasswordCallback) callbacks[0];
//实际上 callback.getPassword()是为null
String clientUsername = callback.getIdentifier();
//其实这一步是应该从数据库中取密码在设置到callback中
String serverPassword = userMap.get(clientUsername);
if (serverPassword != null) {
callback.setPassword(serverPassword);
}
try {
System.out.println(new Date());
Thread.sleep(1000L);
} catch (InterruptedException e) {
//nothing.
}
}
} /**
* Verify a UsernameToken containing a password digest. It does this by querying a
* CallbackHandler instance to obtain a password for the given username, and then comparing
* it against the received password.
* @param usernameToken The UsernameToken instance to verify
* @throws WSSecurityException on a failed authentication.
*/
protected void verifyDigestPassword(UsernameToken usernameToken,
RequestData data) throws WSSecurityException {
if (data.getCallbackHandler() == null) {
throw new WSSecurityException(WSSecurityException.FAILURE, "noCallback");
}
String user = usernameToken.getName();
String password = usernameToken.getPassword();
String nonce = usernameToken.getNonce();
String createdTime = usernameToken.getCreated();
String pwType = usernameToken.getPasswordType();
boolean passwordsAreEncoded = usernameToken.getPasswordsAreEncoded();
WSPasswordCallback pwCb =
new WSPasswordCallback(user, null, pwType, WSPasswordCallback.USERNAME_TOKEN, data);
try {
data.getCallbackHandler().handle(new Callback[]{pwCb});
} catch (IOException e) {
if (log.isDebugEnabled()) {
log.debug(e);
}
throw new WSSecurityException(
WSSecurityException.FAILED_AUTHENTICATION, null, null, e
);
} catch (UnsupportedCallbackException e) {
if (log.isDebugEnabled()) {
log.debug(e);
}
throw new WSSecurityException(
WSSecurityException.FAILED_AUTHENTICATION, null, null, e
);
}
String origPassword = pwCb.getPassword();
if (origPassword == null) {
if (log.isDebugEnabled()) {
log.debug("Callback supplied no password for: " + user);
}
throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
}
if (usernameToken.isHashed()) {
String passDigest;
if (passwordsAreEncoded) {
passDigest = UsernameToken.doPasswordDigest(nonce, createdTime, Base64.decode(origPassword));
} else {
passDigest = UsernameToken.doPasswordDigest(nonce, createdTime, origPassword);
}
if (!passDigest.equals(password)) {
throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
}
} else {
if (!origPassword.equals(password)) {
throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
}
}
}public class ErrorHandlerInterceptor extends AbstractSoapInterceptor {
public ErrorHandlerInterceptor() {
super(Phase.MARSHAL);
}
public void handleMessage(SoapMessage message) throws Fault {
// 错误原因
Fault fault = (Fault) message.getContent(Exception.class);
// 错误信息
String errorMessage = null;
Throwable cause = null;
if (fault != null) {
errorMessage = fault.getMessage();
cause = fault.getCause();
}
Exchange exchange = message.getExchange();
// wsdl描述
String servicePath = null;
// url与uri
String url = null;
String uri = null;
// 客户端ip
String clientIp = null;
// 用户名
String username = null;
// 密码
String psw = null;
// 服务中的方法
String methodName = null;
// 参数名
Object[] paramNames = null;
// 参数值
Object[] paramValues = null;
if (exchange != null) {
Object object = exchange.get("javax.xml.ws.wsdl.description");
if (object != null) {
servicePath = object.toString();
}
Message inMessage = exchange.getInMessage();
if (inMessage != null) {
HttpServletRequest req = (HttpServletRequest) inMessage
.get("HTTP.REQUEST");
clientIp = getIpAddr(req);
url = (String) inMessage.get("org.apache.cxf.request.url");
uri = (String) inMessage.get("org.apache.cxf.request.uri");
}
W3CDOMStreamWriter w3CDOMStreamWriter = (W3CDOMStreamWriter) inMessage
.get(W3CDOMStreamWriter.class.getName());
if (w3CDOMStreamWriter != null) {
Document document = w3CDOMStreamWriter.getDocument();
if (document != null) {
NodeList usernames = document
.getElementsByTagName("wsse:Username");
NodeList psws = document
.getElementsByTagName("wsse:Password");
if (usernames != null && usernames.getLength() == 1) {
username = usernames.item(0).getTextContent();
}
if (psws != null && psws.getLength() == 1) {
psw = psws.item(0).getTextContent();
}
NodeList body = document.getElementsByTagName("soap:Body");
Node method = body.item(0).getFirstChild();
if (method != null) {
methodName = method.getNodeName();
String[] methods = methodName.split(":");
if (methods != null && methods.length == 2) {
methodName = methods[1];
}
NodeList args = method.getChildNodes();
paramNames = getParam(args, true);
paramValues = getParam(args, false);
}
}
}
}
System.out.println(cause);
System.out.println(errorMessage);
System.out.println(servicePath);
System.out.println(url);
System.out.println(uri);
System.out.println(clientIp);
System.out.println(username);
System.out.println(psw);
System.out.println(methodName);
System.out.println("---names--");
System.out.print(getParam(paramNames) + "\t");
System.out.println("\n---values--");
System.out.print(getParam(paramValues) + "\t");
}public class ClientPasswordCallback implements CallbackHandler {
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
WSPasswordCallback callback = (WSPasswordCallback) callbacks[0];
System.out.println(new Date());
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
callback.setPassword("clientpass");
}
}<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<bean id="clientPasswordCallback" class="com.cxfdemo.ws.client.ClientPasswordCallback"></bean>
<bean id="wss4jOutInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">
<constructor-arg>
<map>
<!-- 用户认证(明文密码) -->
<entry key="action" value="UsernameToken"/>
<entry key="user" value="client"/>
<entry key="passwordType" value="PasswordText"/>
<entry key="passwordCallbackRef" value-ref="clientPasswordCallback"/>
</map>
</constructor-arg>
</bean>
<jaxws:client id="client" address="http://localhost:8888/CXFDemo/webservice/helloWorld"
serviceClass="com.cxfdemo.ws.service.HelloWorld">
<jaxws:inInterceptors>
<bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
</jaxws:inInterceptors>
<jaxws:outInterceptors>
<bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
<ref bean="wss4jOutInterceptor"/>
</jaxws:outInterceptors>
</jaxws:client>
<jaxws:client id="updateFile" address="http://localhost:8888/CXFDemo/webservice/updateFile"
serviceClass="com.cxfdemo.ws.service.HelloWorld">
</jaxws:client>
<!-- 对所有的服务配置超时机制 只对服务名为{http://service.ws.cxfdemo.com/}HelloWorldService的服务生效. -->
<http-conf:conduit name="*.http-conduit">
<!-- ConnectionTimeout获取连接超时 ReceiveTimeout获取结果超时-->
<http-conf:client ConnectionTimeout="15000" ReceiveTimeout="30000"/>
</http-conf:conduit>ApplicationContext context = new ClassPathXmlApplicationContext("client_Spring.xml");
HelloWorld helloService = context.getBean("client",HelloWorld.class);
String response = helloService.sayHi("Peter");
System.out.println(response);private static final QName SERVICE_NAME = new QName("http://service.ws.cxfdemo.com/", "HelloWorldServiceService");URL wsdlURL = HelloWorldServiceService.WSDL_LOCATION;
if (args.length > 0 && args[0] != null && !"".equals(args[0])) {
File wsdlFile = new File(args[0]);
try {
if (wsdlFile.exists()) {
wsdlURL = wsdlFile.toURI().toURL();
} else {
wsdlURL = new URL(args[0]);
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
HelloWorldServiceService ss = new HelloWorldServiceService(wsdlURL, SERVICE_NAME);
HelloWorld port = ss.getHelloWorldServicePort();
org.apache.cxf.endpoint.Client client = ClientProxy.getClient(port);
org.apache.cxf.endpoint.Endpoint cxfEndpoint = client.getEndpoint();
Map<String, Object> outProps = new HashMap<String, Object>();
outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
outProps.put(WSHandlerConstants.USER, "client");
outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, ClientPasswordCallback.class.getName());
WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps);
cxfEndpoint.getOutInterceptors().add(wssOut);
System.out.println("Invoking sayHi...");
java.lang.String _sayHi_text = "tttttt";
java.lang.String _sayHi__return = port.sayHi(_sayHi_text);
System.out.println("sayHi.result=" + _sayHi__return);标签:webservice cxf 安全
原文地址:http://blog.csdn.net/zjw10wei321/article/details/39889823