项目需求对webservice接口进行加密,然后网上看到wss4j,于是翻阅资料写的歌DEMO
首先JAR包引入
<!-- CXF 3.2.4 START-->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxws</artifactId>
<version>3.2.6</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-ws-security</artifactId>
<version>3.2.6</version>
</dependency>
然后服务端CXF的配置类
import com.aadata.datacenter.dataManage.webservice.MessagePackService;
import org.apache.cxf.Bus;
import org.apache.cxf.binding.soap.saaj.SAAJInInterceptor;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor;
import org.apache.wss4j.dom.WSConstants;
import org.apache.wss4j.dom.handler.WSHandlerConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.xml.ws.Endpoint;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class CxfConfig {
@Autowired
private MessagePackService messagePackService;
// 注意:要修改ServletRegistrationBean的别名,如果不修改会和前端控制器的Servlet名称冲突,导致spring找不到相应的servlet
@Bean(name="CXFServlet")
public ServletRegistrationBean dispatcherServlet() {
return new ServletRegistrationBean(new CXFServlet(), "/webservice/*");// 发布服务名称 localhost:8080/webservice
}
@Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
return new SpringBus();
}
@Bean
public Endpoint getDataManageServer() {
EndpointImpl endpoint = new EndpointImpl(springBus(), messagePackService);
Map<String, Object> inProps = new HashMap<String, Object>();
inProps.put(WSHandlerConstants.USER, "admin");
inProps.put(WSHandlerConstants.ACTION,WSHandlerConstants.USERNAME_TOKEN);//加密类型
inProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_DIGEST); //密码类型为加密
inProps.put(WSHandlerConstants.PW_CALLBACK_CLASS,
ExampleServiceInterceptor.class.getName()); //密码回调函数
endpoint.getInInterceptors().add( new SAAJInInterceptor());
endpoint.getInInterceptors().add(new WSS4JInInterceptor(inProps));
endpoint.publish("/CommonService");
return endpoint;
}
}
服务端的拦截器
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.wss4j.common.ext.WSPasswordCallback;
import javax.security.auth.callback.CallbackHandler;
public class ExampleServiceInterceptor implements CallbackHandler {
private Map<String, String> passwords = new HashMap<String, String>();
public ExampleServiceInterceptor() {
passwords.put("admin1", "password1");//验证信息:用户名+密码,必须与客户端一致才可验证通过
}
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
pc.setPassword(passwords.get(identifier));
}else if (usage == WSPasswordCallback.SIGNATURE) {
// 密钥方式SIGNATURE
pc.setPassword(passwords.get(identifier));
}
}
}
}
客户端拦截器
import org.apache.wss4j.common.ext.WSPasswordCallback;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
public class ExampleServiceClientInterceptor implements CallbackHandler {
private Map<String, String> passwords = new HashMap<String, String>();
public ExampleServiceClientInterceptor() {
passwords.put("admin1", "password1");
}
public void handle(Callback[] callbacks) {
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
pc.setPassword(passwords.get(identifier));/
}else if (usage == WSPasswordCallback.SIGNATURE) {// 密钥方式SIGNATURE
pc.setPassword(passwords.get(identifier));
}
}
}
}
客户端调用主方法
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import javax.xml.namespace.QName;
import org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;
import org.apache.wss4j.dom.WSConstants;
import org.apache.wss4j.dom.handler.WSHandlerConstants;
public class test020 {
public static void main(String[] args) throws Exception {
JaxWsDynamicClientFactory dcflient=JaxWsDynamicClientFactory.newInstance();
Client client=dcflient.createClient("http://localhost:8999/webservice/CommonService?wsdl");
Map<String, Object> outProps = new HashMap<String, Object>();
outProps.put(WSHandlerConstants.ACTION,
WSHandlerConstants.USERNAME_TOKEN);
outProps.put(WSHandlerConstants.USER, "admin1");
outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_DIGEST);
// 指定在调用远程ws之前触发的回调函数WsClinetAuthHandler,其实类似于一个拦截器
outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS,
ExampleServiceClientInterceptor.class.getName());
ArrayList list = new ArrayList();
// 添加cxf安全验证拦截器,必须
list.add(new SAAJOutInterceptor());
list.add(new WSS4JOutInterceptor(outProps));
client.getOutInterceptors().addAll(list);
QName name=new QName("http://webservice.dataManage.datacenter.aadata.com/","sayHello");
Object[] objects=client.invoke(name,"wss4j+cxf实现WS-Security");
System.out.println(objects[0].toString());
}
}
CXF方式生成客户端调用
CXF 客户端生成client对象命令:
C:\Users\Administrator\Downloads\apache-cxf-3.2.6\bin> wsdl2java -p com.aadata -d C:\Users\Administrator\Desktop\test -client http://192.168.2.20:8088/WSSSS/webservice/CommonService/sayHello?wsdl
对应的客户端拦截器
import org.apache.wss4j.common.ext.WSPasswordCallback;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import java.util.HashMap;
import java.util.Map;
public class ExampleServiceClientInterceptor implements CallbackHandler {
private Map<String, String> passwords = new HashMap<String, String>();
public ExampleServiceClientInterceptor() {
passwords.put("HuQiuTest", "Ceshi_123456");
}
public void handle(Callback[] callbacks) {
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
System.out.println(passwords.containsKey(identifier)+"="+passwords.get(identifier)+"-"+identifier);
pc.setPassword(passwords.get(identifier));// //▲【这里非常重要】▲
}else if (usage == WSPasswordCallback.SIGNATURE) {// 密钥方式SIGNATURE
pc.setPassword(passwords.get(identifier));// //▲【这里非常重要】▲
}
}
}
}
对应客户端类(WSHandlerConstants.ACTION值有所修改,对应服务端也要修改):
package wsss;
/**
* Please modify this class to meet your needs
* This class is not complete
*/
import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;
import org.apache.wss4j.dom.WSConstants;
import org.apache.wss4j.dom.handler.WSHandlerConstants;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;
/**
* This class was generated by Apache CXF 3.2.6
* 2021-01-22T15:14:43.233+08:00
* Generated source version: 3.2.6
*
*/
public final class MessagePackService_MessagePackServiceImplPort_Client {
private static final QName SERVICE_NAME = new QName("http://webservice.dataManage.datacenter.aadata.com/", "MessagePackService");
private MessagePackService_MessagePackServiceImplPort_Client() {
}
public static void main(String args[]) throws Exception {
URL wsdlURL = wsss.MessagePackService_Service.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();
}
}
wsss.MessagePackService_Service ss = new wsss.MessagePackService_Service(wsdlURL, SERVICE_NAME);
wsss.MessagePackService port = ss.getMessagePackServiceImplPort();
org.apache.cxf.endpoint.Client client = ClientProxy.getClient(port);
Endpoint cxfEp = client.getEndpoint();
Map<String, Object> outProps = new HashMap<String, Object>();
outProps.put(WSHandlerConstants.ACTION,
WSHandlerConstants.USERNAME_TOKEN+" "+WSHandlerConstants.TIMESTAMP);
outProps.put(WSHandlerConstants.USER, "Test");
outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
// 指定在调用远程ws之前触发的回调函数WsClinetAuthHandler,其实类似于一个拦截器
outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS,
wsss.ExampleServiceClientInterceptor.class.getName());
cxfEp.getOutInterceptors().add(new WSS4JOutInterceptor(outProps));
{
System.out.println("Invoking sayHello...");
String _sayHello_userName = "测试方法";
String _sayHello__return = port.sayHello(_sayHello_userName);
System.out.println("sayHello.result=" + _sayHello__return);
}
System.exit(0);
}
}