背景
最近做了一个对接的工作,要跟一个比较老的系统对接,采用的是WebService技术。因为用户所处的行业对于安全的要求比较高以及系统建设时间比较久,所以还是有不少采用WebService的对接方式。但本次对二级跟以往的都不太一样,对于接口命名有比较严格的限制,而且对于传输的xml数据要增加认证信息。这也就意味着服务端要对这种自定义的数据结构进行解析和鉴权。客户端要将账户信息手动组装到xml中。
解决方案
技术框架
这个单独拿来说一下是因为,我们查到的不少资料都是实用CXF框架实现的这种需求,而我们使用的是spring-boot-starter-web-services
,至今我还真是没找到现成完整的解决方案,所以就在这里记录一下。另外我看有些人是直接组装的字符串返回去,在此膜拜一下毅力,但这个非常的不提倡,太难以维护了。
XSD定义
服务端
AuthInterceptor.java
public class AuthInterceptor extends EndpointInterceptorAdapter {
@Value("${userName:name}")
private String serverUserName;
@Value("${userPassword:psw}")
private String serverPassoword;
@Override
public boolean handleResponse(MessageContext messageContext, Object endpoint) throws Exception {
SaajSoapMessage soapResponse = (SaajSoapMessage) messageContext.getResponse();
SOAPMessage soapMessage = soapResponse.getSaajMessage();
SOAPBody body = null;
try {
body = soapMessage.getSOAPBody();
NodeList chlidrenNodes = body.getChildNodes();
for (int i = 0; i < chlidrenNodes.getLength(); i++) {
Node node = chlidrenNodes.item(i);
node.setPrefix(systemShortName);
NodeList subNodes = node.getChildNodes();
for (int j = 0; j < subNodes.getLength(); j++) {
Node subNode = subNodes.item(j);
subNode.setPrefix(systemShortName);
}
}
Iterator<SOAPElement> iterator=body.getChildElements();
while(iterator.hasNext()){
SOAPElement element=iterator.next();
//这里可以自己定制空间,ps默认的空间是n2要想去掉也在这处理
element.removeNamespaceDeclaration("ns2");
}
} catch (SOAPException e) {
log.error("getSOAPbody error:{}", e.getMessage(), e);
}
soapMessage.saveChanges();
return super.handleRequest(messageContext, endpoint);
}
@Override
public boolean handleRequest(MessageContext messageContext, Object endpoint) throws Exception {
SaajSoapMessage soapRequest = (SaajSoapMessage) messageContext.getRequest();
SOAPMessage soapMessage = soapRequest.getSaajMessage();
SOAPHeader header = null;
try {
header = soapMessage.getSOAPHeader();
} catch (SOAPException e) {
log.error("getSOAPheader error:{}", e.getMessage(), e);
}
NodeList chlidrenNodes = securityNode.item(0).getChildNodes();
String userName = "";
String password = "";
for (int i = 0; i < chlidrenNodes.getLength(); i++) {
Node node = chlidrenNodes.item(i);
if (node.getNodeName().contains("username")) {
userName = node.getTextContent().trim();
}
if (node.getNodeName().contains("password")) {
password = node.getTextContent().trim();
}
}
if (serverUserName.equals(userName) && serverPassword.equals(password)) {
log.debug("admin auth success");
} else {
SOAPException soapException = new SOAPException("认证错误");
log.debug("admin auth failed");
throw new Exception(soapException);
}
soapMessage.saveChanges();
return super.handleRequest(messageContext, endpoint);
}
}
WsServiceConfig.java
@EnableWs
@Configuration
public class WsServiceConfig extends WsConfigurerAdapter {
@Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, "/ws/*");
}
@Bean(name = "endpoint")
public DefaultWsdl11Definition defaultWsdl11DefinitionData(XsdSchema updateSchema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
//根据业务要处理
wsdl11Definition.setSchema(updateSchema);
return wsdl11Definition;
}
@Bean
public XsdSchema updateSchema() {
return new SimpleXsdSchema(new ClassPathResource("xx.xsd"));
}
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
//自定义认证拦截器
interceptors.add(authInterceptor());
}
@Bean
public AuthInterceptor authInterceptor(){
return new AuthInterceptor();
}
}
客户端
WsAuthInterceptor.java
@Slf4j
@Component
public class WsAuthInterceptor extends ClientInterceptorAdapter{
private static final String SECURITY_NODE_NAME="security";
@Value("${username:username}")
private String username;
@Value("${password:password}")
private String password;
@Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
SaajSoapMessage soapRequest = (SaajSoapMessage) messageContext.getRequest();
SOAPMessage soapMessage = soapRequest.getSaajMessage();
SOAPHeader header = null;
try {
header = soapMessage.getSOAPHeader();
SOAPElement secrity=header.addChildElement(SECURITY_NODE_NAME);
//xx是命名空间
SOAPElement nameElement=secrity.addChildElement("username","xx");
SOAPElement passwordElement=secrity.addChildElement("password","xx");
nameElement.setValue(systemStortName);
passwordElement.setValue(SM3EncryptUtil.encrypt(costumerPassword));
soapMessage.saveChanges();
} catch (SOAPException e) {
log.error("getSOAPheader error:{}", e.getMessage(), e);
}
return super.handleRequest(messageContext);
}
}
在使用的时候这样使用,client就是那个extend WebServiceGatewaySupport的实例。
ClientInterceptor[] interceptors=new ClientInterceptor[]{customIterceptor};
client.setInterceptors(interceptors);
总结
专业填坑。