我们创建的webService服务要有安全性,不能被人随便调用,要有用户认证,在传输过程中还要加密。
Java WebService_cxf (1) 入门案例 中的创建的服务任何人都可以调用,这显然是不安全的,我们在入门案例基础上加入用户的认证。
服务器端:
将spring-cxf-service.xml修改成:
密码回调函数类:
package shzj.web.webService.service;
import org.apache.ws.security.WSPasswordCallback;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import java.io.IOException;
public class DemoServerPasswordCallback implements CallbackHandler {
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
String pw = pc.getPassword();
String idf = pc.getIdentifier();
//特别注意的是 这里的pw肯是null, 无论你password是否传值。cxf 2.4之后,
//密码的比较是框架自动帮我们完成,因此不需要我们获取传递过来的密码,如果你
//一定要查看密码的话,可以通过new String(pc.getKey())获取。在该回调函数中
//我们只需要使用 idf 从数据库中查询出密码,使用pc.setPassword()方法将密码设
//置进去,框架获取的的密码后会进行比较,并通过抛出异常的方式提示验证出错。
System.out.println("密码是: " + pw);
System.out.println("身份是: " + idf);
//根据idf 我们从数据中查询出的密码 假如是 "abc"
pc.setPassword("abc");
}
}
发布
第一种实现方式:通过配置客户端来调用服务
spring-cxf-client.xml修改为:
package shzj.web.webService.service;
import org.apache.ws.security.WSPasswordCallback;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import java.io.IOException;
public class DemoClientPasswordCallback implements CallbackHandler {
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
String ident = "I";
String passwd = "2";
pc.setPassword(passwd);
pc.setIdentifier(ident);
}
}
测试:
@Test
public void text2() {
ApplicationContext factory = new ClassPathXmlApplicationContext("/spring-cxf-client.xml");
CxfDemo client = (CxfDemo)factory.getBean("DemoClient");
String response = client.helloWord("小米");
System.out.println("返回: " + response);
}
I
2
小米
很显然是认证出错了。应为密码出错。如果密码是abc,结果就是:返回: hello 小米!!!
cxf框架验证出错是通过抛出异常提示我们的,那么经常会抛出的异常有:
第二种实现方式:通过JaxWsProxyFactoryBean代理类来设定服务处理类和服务地址,无须额外的客户端配置。
@Test
public void text3() {
// 以下和服务端配置类似,不对,应该说服务端和这里的安全验证配置一致
Map map = new HashMap();
map.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
map.put(WSHandlerConstants.USER, "admin");
map.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
// 指定在调用远程ws之前触发的回调函数WsClinetAuthHandler,其实类似于一个拦截器
map.put(WSHandlerConstants.PW_CALLBACK_CLASS, DemoClientPasswordCallback.class.getName());
ArrayList list = new ArrayList();
// 添加cxf安全验证拦截器,必须
list.add(new SAAJOutInterceptor());
list.add(new WSS4JOutInterceptor(map));
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
// WebServiceSample服务端接口实现类
factory.setServiceClass(CxfDemo.class);
// 设置ws访问地址
factory.setAddress("http://localhost:8080/shzj/webservice/helloWorld?wsdl");
//注入拦截器,用于加密安全验证信息
factory.getOutInterceptors().addAll(list);
CxfDemo service = (CxfDemo) factory.create();
String response = service.helloWord("小米");
System.out.println("返回: " + response);
}