使用 ws-security 的身份验证 soap 请求的异常
Exception of an authentication soap request with ws-security
从昨天开始,我就尝试通过 ws-security 将安全性集成到我的 Web 服务 soap(使用 apache cxf)中。为此,我有以下配置:
--my endppoint 及其拦截器配置:
@Configuration
@ImportResource({"classpath:META-INF/cxf/cxf.xml"})
@ComponentScan(basePackages ={"com.nhit.dev"})
public class MyConfig extends SpringBootServletInitializer{
@Bean
public IServicesWeb momoService() {
return new MomoServices();
}
@Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
return new SpringBus();
}
@Bean
public ServletRegistrationBean cxfServlet() {
ServletRegistrationBean servlet = new ServletRegistrationBean(new CXFServlet(), "/services/*");
servlet.setLoadOnStartup(1);
return servlet;
}
@Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), momoService());
endpoint.publish("/momo");
Map<String, Object> inProps = new HashMap<String, Object>();
inProps.put("action", "UsernameToken");
inProps.put("passwordType", "PasswordText");
inProps.put("passwordCallbackClass", "com.nhit.dev.mobilepayment.web.WsPwdCallBack");
endpoint.getInInterceptors().add(new WSS4JInInterceptor(inProps));
Map<String, Object> outProps = new HashMap<String, Object>();
outProps.put("action", "UsernameToken");
outProps.put("user", "abc");
outProps.put("passwordType", "PasswordText");
outProps.put("passwordCallbackClass", "com.nhit.dev.mobilepayment.web.WsPwdCallBack");
endpoint.getOutInterceptors().add(new WSS4JOutInterceptor(outProps));
return endpoint;
}
}
--我的 PasswordCallBack class 处理程序:
public class WsPwdCallBack implements CallbackHandler{
protected final Log logger = LogFactory.getLog(getClass());
private Map<String, String> passwords = new HashMap<String, String>();
public WsPwdCallBack() {
passwords.put("abc", "abc");
passwords.put("xyz", "xyz");
}
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
String pass = passwords.get(pc.getIdentifier());
if (pass != null) {
pc.setPassword(pass);
return;
}
}
}
}
--最后是来自 soapUI 的 soap 请求:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://webservices.web.mobilepayment.dev.nhit.com/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<soapenv:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" soapenv:mustUnderstand="1">
<wsse:UsernameToken wsu:Id="UsernameToken-87b7b0c5-31fe-4a01-b333-f9ca564ded57">
<wsse:Username>xyz</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">TlPGdyb/NOoeA2KMO0n6DbmA0AA=</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">FCG+tTtuZXguO8nUQUQeIQ==</wsse:Nonce>
<wsu:Created>2016-12-08T12:12:00.Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<web:creerDevise>
<!--Optional:-->
<libelle>Livre</libelle>
</web:creerDevise>
</soapenv:Body>
</soapenv:Envelope>
当我从 SOAPUI 执行这个请求时,我收到以下错误:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode xmlns:ns1="http://ws.apache.org/wss4j">ns1:SecurityError</faultcode>
<faultstring>A security error was encountered when verifying the message</faultstring>
</soap:Fault>
</soap:Body>
</soap:Envelope>
因此,我将查看部署我的应用程序的 .ear 存档的 wildfly 日志;在那里我看到了这个例外:
Caused by: org.apache.wss4j.common.ext.WSSecurityException: The message has expired
请帮助我修复它,我是 ws-security 的新手。我不知道如何解决这个异常。
WSS4J 默认对 UsernameToken 的创建时间戳强制实施 5 分钟的时间限制。因此,在 SOAP UI 中,如果自上次创建 UsernameToken 片段以来已过去 5 分钟以上,则需要重新创建它。或者,您可以配置 WSS4J 以允许更长的到期时间限制。
从昨天开始,我就尝试通过 ws-security 将安全性集成到我的 Web 服务 soap(使用 apache cxf)中。为此,我有以下配置: --my endppoint 及其拦截器配置:
@Configuration
@ImportResource({"classpath:META-INF/cxf/cxf.xml"})
@ComponentScan(basePackages ={"com.nhit.dev"})
public class MyConfig extends SpringBootServletInitializer{
@Bean
public IServicesWeb momoService() {
return new MomoServices();
}
@Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
return new SpringBus();
}
@Bean
public ServletRegistrationBean cxfServlet() {
ServletRegistrationBean servlet = new ServletRegistrationBean(new CXFServlet(), "/services/*");
servlet.setLoadOnStartup(1);
return servlet;
}
@Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), momoService());
endpoint.publish("/momo");
Map<String, Object> inProps = new HashMap<String, Object>();
inProps.put("action", "UsernameToken");
inProps.put("passwordType", "PasswordText");
inProps.put("passwordCallbackClass", "com.nhit.dev.mobilepayment.web.WsPwdCallBack");
endpoint.getInInterceptors().add(new WSS4JInInterceptor(inProps));
Map<String, Object> outProps = new HashMap<String, Object>();
outProps.put("action", "UsernameToken");
outProps.put("user", "abc");
outProps.put("passwordType", "PasswordText");
outProps.put("passwordCallbackClass", "com.nhit.dev.mobilepayment.web.WsPwdCallBack");
endpoint.getOutInterceptors().add(new WSS4JOutInterceptor(outProps));
return endpoint;
}
}
--我的 PasswordCallBack class 处理程序:
public class WsPwdCallBack implements CallbackHandler{
protected final Log logger = LogFactory.getLog(getClass());
private Map<String, String> passwords = new HashMap<String, String>();
public WsPwdCallBack() {
passwords.put("abc", "abc");
passwords.put("xyz", "xyz");
}
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
String pass = passwords.get(pc.getIdentifier());
if (pass != null) {
pc.setPassword(pass);
return;
}
}
}
}
--最后是来自 soapUI 的 soap 请求:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://webservices.web.mobilepayment.dev.nhit.com/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<soapenv:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" soapenv:mustUnderstand="1">
<wsse:UsernameToken wsu:Id="UsernameToken-87b7b0c5-31fe-4a01-b333-f9ca564ded57">
<wsse:Username>xyz</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">TlPGdyb/NOoeA2KMO0n6DbmA0AA=</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">FCG+tTtuZXguO8nUQUQeIQ==</wsse:Nonce>
<wsu:Created>2016-12-08T12:12:00.Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<web:creerDevise>
<!--Optional:-->
<libelle>Livre</libelle>
</web:creerDevise>
</soapenv:Body>
</soapenv:Envelope>
当我从 SOAPUI 执行这个请求时,我收到以下错误:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode xmlns:ns1="http://ws.apache.org/wss4j">ns1:SecurityError</faultcode>
<faultstring>A security error was encountered when verifying the message</faultstring>
</soap:Fault>
</soap:Body>
</soap:Envelope>
因此,我将查看部署我的应用程序的 .ear 存档的 wildfly 日志;在那里我看到了这个例外:
Caused by: org.apache.wss4j.common.ext.WSSecurityException: The message has expired
请帮助我修复它,我是 ws-security 的新手。我不知道如何解决这个异常。
WSS4J 默认对 UsernameToken 的创建时间戳强制实施 5 分钟的时间限制。因此,在 SOAP UI 中,如果自上次创建 UsernameToken 片段以来已过去 5 分钟以上,则需要重新创建它。或者,您可以配置 WSS4J 以允许更长的到期时间限制。