CXF的webService已经创建好,但没有安全可言,毕竟这是Internet服务呀。
CXF给了一个很完整的安全架构,但CXF给出的ws_security DEMO太复杂了,又是password jks X509 Timestamp。 我试了很多次都没有成功。化繁为简,只实现一个user password好了。下面开始
编写cxf.xml在原来的bean的地方声明一下就可以了
Java代码
<bean id="WSS4JInInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<constructor-arg>
<map>
<entry key="action" value="UsernameToken" />
<entry key="passwordType" value="PasswordText" />
<entry key="passwordCallbackClass"
value="com.xxxx.Service.ServerPasswordCallback" />
</map>
</constructor-arg>
</bean>
<jaxws:endpoint id="chartScreen" implementor="#chartScreenService"
address="/ChartScreenService" >
<jaxws:inInterceptors>
<bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor" />
<ref bean="WSS4JInInterceptor" />
</jaxws:inInterceptors>
</jaxws:endpoint>
<bean id="WSS4JInInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<constructor-arg>
<map>
<entry key="action" value="UsernameToken" />
<entry key="passwordType" value="PasswordText" />
<entry key="passwordCallbackClass"
value="com.mms.webservice.test.ServerPasswordCallback" />
</map>
</constructor-arg>
</bean>
<jaxws:endpoint id="helloWorld"
implementor="com.mms.webservice.HelloWorldImpl"
address="/HelloWorld">
<jaxws:inInterceptors>
<!--
<bean
class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor" />
<bean
class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<constructor-arg>
<map>
<entry key="action" value="UsernameToken" />
<entry key="passwordType" value="PasswordText" />
<entry key="passwordCallbackClass"
value="com.mms.webservice.test.ServerPasswordCallback" />
</map>
</constructor-arg>
</bean>
-->
<ref bean="WSS4JInInterceptor" />
</jaxws:inInterceptors>
</jaxws:endpoint>
WSS4JInInterceptor就是我们要定义的东东了。CXf已经帮你写好了。设置属性就可以了。里面属性值挺多的,CXF的文档就是太简单了,opensource的弊病!属性值就查API吧。
下面需要写server端的密码回调函数,验证logic就在这里定义了。
Java代码
public class ServerPasswordCallback implements CallbackHandler {
private Map<String, String> passwords = new HashMap<String, String>();
public ServerPasswordCallback(){
passwords.put("admin", "admin");
passwords.put("test", "test");
}
@Override
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
if(!passwords.containsKey(pc.getIdentifier()))
throw new WSSecurityException("user not match");
String pass = passwords.get(pc.getIdentifier());
String pwd = pc.getPassword();
if (pwd == null || !pwd.equals(pass)){
throw new WSSecurityException("password not match");
}
}
}
}就此server端的验证就全部ok了。这时再调用原来的调用程序就会报ws_security错误了。
下面给出Client验证程序
其实就是在soapheader上加相应内容。也需要用到inInterceptors
Java代码
public class ClientPasswordCallback implements CallbackHandler {
private Map<String, String> passwords =
new HashMap<String, String>();
public ClientPasswordCallback(){
passwords.put("admin", "admin");
passwords.put("test", "test");
}
@Override
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
int usage = pc.getUsage();
if(!passwords.containsKey(pc.getIdentifier()))
throw new WSSecurityException("user not exists ");
String pass = passwords.get(pc.getIdentifier());
if (usage == WSPasswordCallback.USERNAME_TOKEN && pass != null) {
pc.setPassword(pass);
return;
}
}
}
}
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
Map<String, Object> outProps = new HashMap<String, Object>();
outProps.put(WSHandlerConstants.ACTION,
WSHandlerConstants.USERNAME_TOKEN);
outProps.put(WSHandlerConstants.USER, "admin");
outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS,
ClientPasswordCallback.class.getName());
WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps);
factory.getOutInterceptors().add(wssOut);
factory.getOutInterceptors().add(new SAAJOutInterceptor());
factory.setServiceClass(IChartScreenService.class);
factory.setAddress("http://localhost:8080/ECFlight/service/ChartScreenService");
IChartScreenService service = (IChartScreenService) factory.create();
也可采用spring 配置:
<!-- wssecurity -->
<bean id="clientPasswordCallback" class="com.evermoresw.megp.utilities.ClientPasswordCallback" />
<bean id="wsOutInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">
<constructor-arg>
<map>
<entry key="action" value="UsernameToken" />
<entry key="passwordType" value="PasswordText" />
<entry key="user" value="admin" />
<entry key="passwordCallbackRef">
<ref bean="clientPasswordCallback" />
</entry>
</map>
</constructor-arg>
</bean>
<jaxws:client id="client_testService" serviceClass="com.evermore.moa.service.Test"
address="http://localhost:8080/cxfTestServer/service/testService">
<jaxws:outInterceptors>
<bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor" />
<ref bean="wsOutInterceptor"/>
</jaxws:outInterceptors>
</jaxws:client>
问题:Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/xml/security/Init
加上:xmlsec-1.4.3.jar包
For WS-Security support:
- bcprov-jdk15.jar
- xalan.jar
- serializer.jar
- wss4j.jar
- xmlsec.jar