记一次webservice 的ws-security开发过程

http://www.blogjava.net/zljpp/archive/2012/04/15/374371.html

ws-security对验证的过程是支持多种方式的,
先写个UsernameToken的

服务端接口
@WebService()
public interface WebServiceSample {
String say(String name);

}
实现类
public class SimpleServiceImpl implements WebServiceSample{

public String say(String name) {
return "hello zhangtao";
}

}
webxml中:
CXF是需要spring支持的
<context-param>
<param-name>contextConfigLocation</param-name>
<!--ws-context.xml(必须)是cxf配置文件, wssec.xml可选,作用可以打印出加密信息类容 -->
<param-value>WEB-INF/ws-context.xml,WEB-INF/wssec.xml</param-value>
</context-param>
还要配置CXF
<url-pattern>/services/*</url-pattern>
是webservice的发布目录

<load-on-startup>0</load-on-startup>
2)当值为0或者大于0时,表示容器在应用启动时就加载并初始化这个servlet;
3)当值小于0或者没有指定时,则表示容器在该servlet被选择时才会去加载。
4)正数的值越小,该servlet的优先级越高,应用启动时就越先加载。
5)当值相同时,容器就会自己选择顺序来加载。
///////////////////////
<servlet>
<servlet-name>CXFServlet</servlet-name>
<display-name>CXF Servlet</display-name>
<servlet-class>
org.apache.cxf.transport.servlet.CXFServlet
</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>


ws-context.xml

<jaxws:endpoint id="webServiceSample" address="/WebServiceSample"
implementor="server.service.impl.SimpleServiceImpl">
<!--inInterceptors表示被外部调用时,调用此拦截器 -->
<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="action" value="UsernameToken Timestamp" /> 设置密码类型为加密
<entry key="passwordType" value="PasswordDigest" /> -->
<entry key="passwordCallbackClass" value="server.security.WsAuthHandler" />
</map>
</constructor-arg>
</bean>
</jaxws:inInterceptors>
</jaxws:endpoint>

这里直接就配置实现类了,没看见接口。endpoint的id跟接口名也是没关系的。
配置了两个jaxws:inInterceptors。就是在请求的时候触发
比如打上地址http://zt-pc:8080/ws-security69/services/WebServiceSample
则输出了
2014-6-11 15:05:10 org.apache.cxf.interceptor.AbstractLoggingInterceptor log
信息: Inbound Message
----------------------------
ID: 1
Address: http://zt-pc:8080/ws-security69/services/WebServiceSample?wsdl
Http-Method: GET
Content-Type:
Headers: {Accept=[application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*], accept-encoding=[gzip, deflate], accept-language=[zh-CN], connection=[Keep-Alive], Content-Type=[null], cookie=[JSESSIONID=A87902CD3C03E42431153E2A8EC32D7F; NavWorkFlowTreeSaveStateCookie=%2C%2F1%2F1_2%2C%2F1%2F1_2%2F1_2_1%2C%2F2%2F2_1%2C%2F2%2F2_1%2F2_1_1%2C%2F2%2C%2F1%2F1_1%2F1_1_1%2C%2F1%2F1_1%2C%2F1], host=[zt-pc:8080], referer=[http://zt-pc:8080/ws-security69/services], user-agent=[Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0E; .NET4.0C)]}
可以看见有header了,没有拦截器的时候我记着好像没header。这里用的应该是SOAP协议吧。
<constructor-arg>这是在构造方法中赋值,property是用setter方法赋值(spring的)

wssec.xml用来输出调试信息
<cxf:bus>
<cxf:features>
<cxf:logging />
<wsa:addressing />
</cxf:features>
</cxf:bus>

而在拦截其中指定的自己的类WsAuthHandler,应该是wss4j拦截后交给我了我的类。
实现handle(Callback[] callbacks)此方法
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
String identifier = pc.getIdentifier();
int usage = pc.getUsage();
if (usage == WSPasswordCallback.USERNAME_TOKEN) {// 密钥方式USERNAME_TOKEN
// username token pwd...
// ▲这里的值必须和客户端设的值相同,从cxf2.4.x后校验方式改为cxf内部实现校验,不必自己比较password是否相同
// 请参考:http://cxf.apache.org/docs/24-migration-guide.html的Runtime
// Changes片段
pc.setPassword("testPassword");// ▲【这里非常重要】▲
// ▲PS 如果和客户端不同将抛出org.apache.ws.security.WSSecurityException:
// The
// security token could not be authenticated or
// authorized异常,服务端会认为客户端为非法调用
} else if (usage == WSPasswordCallback.SIGNATURE) {// 密钥方式SIGNATURE
// set the password for client's keystore.keyPassword
// ▲这里的值必须和客户端设的值相同,从cxf2.4.x后校验方式改为cxf内部实现校验,不必自己比较password是否相同;
// 请参考:http://cxf.apache.org/docs/24-migration-guide.html的Runtime
// Changes片段
pc.setPassword("testPassword");// //▲【这里非常重要】▲
// ▲PS:如果和客户端不同将抛出org.apache.ws.security.WSSecurityException:The
// security token could not be authenticated or
// authorized异常,服务端会认为客户端为非法调用
}
//不用做其他操作
}
}
pc.setPassword("testPassword");这是在服务端,这句的意思应该是,比对的时候,把客户端发过来的密码和这个比对

客户端

public static void main(String[] args) {
// 以下和服务端配置类似,不对,应该说服务端和这里的安全验证配置一致
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);
// 指定在调用远程ws之前触发的回调函数WsClinetAuthHandler,其实类似于一个拦截器
outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS,WsClinetAuthHandler.class.getName());

ArrayList list = new ArrayList();
// 添加cxf安全验证拦截器,必须
list.add(new SAAJOutInterceptor());
list.add(new WSS4JOutInterceptor(outProps));

JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
// WebServiceSample服务端接口实现类,这里并不是直接把服务端的类copy过来,具体请参考http://learning.iteye.com/blog/1333223
factory.setServiceClass(WebServiceSample.class);
// 设置ws访问地址
factory.setAddress("http://zt-pc:8080/ws-security69/services/WebServiceSample");
        //注入拦截器,用于加密安全验证信息
factory.getOutInterceptors().addAll(list);
WebServiceSample service = (WebServiceSample) factory.create();
String response = service.say("2012");
System.out.println(response);
}

public class WsClinetAuthHandler implements CallbackHandler {

public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
System.out.println("identifier: " + pc.getIdentifier());
// 这里必须设置密码,否则会抛出:java.lang.IllegalArgumentException: pwd == null
// but a password is needed
pc.setPassword("testPassword");// ▲【这里必须设置密码】▲
}
}
}
factory.getOutInterceptors().addAll(list);在发出请求的时候拦截,添加了两个
安全验证拦截器。并且在wss4j的outInterceptor中new WSS4JOutInterceptor(outProps)
这些参数原来是跟服务器端对应的

运行后就行了
identity没什么用啊,根据控制台输出看到header中
identity和password都是明文的。

你可能感兴趣的:(webservice,CXF,ws-security)