SOAP安全,客户端身份验证问题



我试图连接到TERYT,但我收到了一个关于缺乏授权的错误。WSDL地址-https://uslugaterytws1test.stat.gov.pl/wsdl/terytws1.wsdl

java.lang.IllegalStateException: Failed to execute ApplicationRunner
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:765) [spring-boot-2.7.1.jar:2.7.1]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:752) [spring-boot-2.7.1.jar:2.7.1]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) [spring-boot-2.7.1.jar:2.7.1]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) [spring-boot-2.7.1.jar:2.7.1]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) [spring-boot-2.7.1.jar:2.7.1]
at com.example.teryt.TerytApplication.main(TerytApplication.java:15) [classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_332]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_332]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_332]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_332]
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) [spring-boot-devtools-2.7.1.jar:2.7.1]

引起原因:com.sun.xml.internal.ws.fault.ServerSOAPFaultException:客户端从服务器接收到SOAP fault:处理消息中的安全令牌时出错。请参阅服务器日志以查找有关故障确切原因的更多详细信息。在com.sun.xml.internal.ws.fault.SOAP11Fault.getProtocolException(SOAP11Fault.java:178(~[na:1.8.0_332]在com.sun.xml.internal.ws.fault.SOAPFaultBuilder.createException(SOAPFaltBuilder.java:116(~[na:1.8.0_332]网址:com.sun.xml.internal.ws.client.sei.StubHandler.readResponse(StubHandler.java:238(~[na:1.8.0_332]在com.sun.xml.internal.ws.db.DatabindingImpl.deserializeResponse(DatabindingImpl.java:189(~[na:1.8.0_332]在com.sun.xml.internal.ws.db.DatabindingImpl.deserializeResponse(DatabindingImpl.java:276(~[na:1.8.0_332]网址:com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:104(~[na:1.8.0_332]网址:com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:77(~[na:1.8.0_332]网址:com.sun.xml.internal.ws.client.sei.SEIStub.invoke(SEIStub.java:147(~[na:1.8.0_332]网址:com.sun.proxy.$Proxy83.pobierzlistWojewodztw(未知来源(~[na:na]网址:com.example.teryt.TerytClient.getResponse(TerytClient.java:31(~[classes/:na]在com.example.tteryt.TerytApplicationLambda$lookup$0(TerytApplication.java:20([classes/:na]网址:org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:762([spring-boot-2.7.1.jar:2.7.1]

正确的查询应该是这样的:

<?xml version="1.0" encoding="UTF-8"?>

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"xmlns:ns1=";http://tempuri.org/"gt;soapenv:标头<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"gt;<wsse:UsernameToken wsu:Id=";UsernameToken-2018-01-11T00:16:02+00:00">wsse:UsernameTestPubliczny<wsse:用户名>wsse:Password1234bcd<wsse:密码><wsse:UsernameToken><wsse:Security><wsa:操作xmlns:wsa="http://www.w3.org/2005/08/addressing"gt;http://tempuri.org/ITerytWs1/PobierzListeWojewodztw<wsa:操作><soapenv:标头>soapenv:正文ns1:PobierzListeWojewodztwns1:DataStanu2020-05-18<ns1:DataStanu><ns1:PobierzListeWojewodztw><soapenv:Body><soapenv:Envelope>

TerytApplication.java:

@SpringBootApplication

公共类TerytApplication{

public static void main(String[] args) {
SpringApplication.run(TerytApplication.class, args);
}
@Bean
ApplicationRunner lookup(TerytClient terytClient){
return args -> System.out.println(terytClient.getResponse());
}

}

Client.java:

@Component

公共类TerytClient扩展WebServiceGatewaySupport{

public Boolean getResponse() throws DatatypeConfigurationException {
GregorianCalendar cal = new GregorianCalendar();
cal.setTime(new Date());
XMLGregorianCalendar xCal = DatatypeFactory.newInstance()
.newXMLGregorianCalendar(cal);
ITerytWs1 instance = new TerytWs1().getCustom(new AddressingFeature(true));
Binding binding = ((BindingProvider) instance).getBinding();
List<Handler> handlerList = binding.getHandlerChain();
if (handlerList == null)
handlerList = new ArrayList<Handler>();
handlerList.add(new TerytHeaderHandler("TestPubliczny", "1234abcd"));
binding.setHandlerChain(handlerList);
System.out.println(instance.pobierzListeWojewodztw(xCal));
return instance.czyZalogowany();
}

HeaderHandler.java

@Override
public boolean handleMessage(SOAPMessageContext smc) {
Boolean outboundProperty = (Boolean) smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
try {
SOAPEnvelope envelope = smc.getMessage().getSOAPPart().getEnvelope();
SOAPHeader header = envelope.getHeader();
SOAPElement security = header.addChildElement("Security", "wsse",
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
SOAPElement usernameToken = security.addChildElement("UsernameToken", "wsse");
SOAPElement username = usernameToken.addChildElement("Username", "wsse");
username.addTextNode(wsUser);
SOAPElement password = usernameToken.addChildElement("Password", "wsse");
password.setAttribute("Type",
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");
password.addTextNode(wsPassword);
} catch (Exception e) {
e.printStackTrace();
}
} else {
//This handler does nothing with the response from the Web Service
//even though it should probably check its mustUnderstand headers
SOAPMessage message = smc.getMessage();
}
return outboundProperty;
}
@Override
public boolean handleFault(SOAPMessageContext context) {
// TODO Auto-generated method stub
return false;
}
@Override
public void close(MessageContext context) {
// TODO Auto-generated method stub
}
// Gets the header blocks that can be processed by this Handler instance.
@Override
public Set<QName> getHeaders() {
QName securityHeader = new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
"Security");
HashSet<QName> headers = new HashSet<QName>();
headers.add(securityHeader);
return headers;
}

}

如果有任何帮助和建议,我将不胜感激。

在构建WS-Security标头时,我建议您使用Apache WSS4J。我附上了一个我经常使用的代码示例。

import lombok.extern.slf4j.Slf4j;
import org.apache.wss4j.dom.WSConstants;
import org.apache.wss4j.dom.message.WSSecHeader;
import org.apache.wss4j.dom.message.WSSecTimestamp;
import org.apache.wss4j.dom.message.WSSecUsernameToken;
import org.w3c.dom.Element;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
@Slf4j
public class ClientWSSecurityUsernameTokenHandler implements
SOAPHandler<SOAPMessageContext> {
public ClientWSSecurityUsernameTokenHandler(
String userName, String password) {
_userName = userName;
_password = password;
}
@Override
public Set<QName> getHeaders() {
final String NAMESPACE_URI =
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
final String LOCAL_PART = "Security";
final String PREFIX = "wsse";
final QName wsSecurity = new QName(NAMESPACE_URI, LOCAL_PART, PREFIX);
final Set<QName> headers = new HashSet<QName>();
headers.add(wsSecurity);
return headers;
}
@Override
public boolean handleMessage(SOAPMessageContext soapMessageContext) {
Boolean outboundProperty = (Boolean) soapMessageContext.get(
MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty) {
try {
SOAPMessage soapMessage = soapMessageContext.getMessage();
SOAPPart soapPart = soapMessage.getSOAPPart();
SOAPHeader soapHeader = soapMessage.getSOAPHeader();
if (null == soapHeader) {
SOAPEnvelope soapEnvelope = soapPart.getEnvelope();
soapHeader = soapEnvelope.addHeader();
}
WSSecHeader wsSecHeader = new WSSecHeader(soapPart);
Element securityElement =
wsSecHeader.insertSecurityHeader();
_addUsernamePassword(soapMessageContext, soapPart, wsSecHeader);
WSSecTimestamp wsSecTimeStamp = new WSSecTimestamp(wsSecHeader);
wsSecTimeStamp.build();
_appendSecurityHeader(soapHeader, securityElement);
_logSOAPMessage(soapMessageContext.getMessage());
}
catch (Exception exception) {
log.error(exception.getMessage(), exception);
}
}
return true;
}
private String getPassword() {
return _password;
}
private String getUserName() {
return _userName;
}
@Override
public boolean handleFault(SOAPMessageContext soapMessageContext) {
_logSOAPMessage(soapMessageContext.getMessage());
return true;
}
@Override
public void close(MessageContext messageContext) {
}
private String _userName;
private String _password;
private void _appendSecurityHeader(
SOAPHeader soapHeader, Element securityElement) {
soapHeader.removeChild(securityElement);
soapHeader.appendChild(securityElement);
}
private void _addUsernamePassword(
SOAPMessageContext soapMessageContext, SOAPPart soapPart,
WSSecHeader wsSecHeader) {
log.debug("Adding Username Token for the user: {}", getUserName());
WSSecUsernameToken usernameToken = new WSSecUsernameToken(wsSecHeader);
usernameToken.setUserInfo(getUserName(), getPassword());
usernameToken.setPasswordType(WSConstants.PASSWORD_TEXT);
usernameToken.addCreated();
usernameToken.addNonce();
usernameToken.build();
}
private void _logSOAPMessage(SOAPMessage message) {
if (log.isDebugEnabled()) {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
try {
message.writeTo(bout);
String msg = bout.toString("UTF-8");
log.debug("Start of SOAP Message");
log.debug(msg);
log.debug("End of SOAP Message");
}
catch (SOAPException | IOException exception) {
if (log.isErrorEnabled()) {
log.error(exception.getMessage(), exception);
}
}
}
}
}

源代码1-SOAP处理程序ClientWSSecurityUsernameTokenHandler

下面的代码显示了如何将处理程序添加到链中。

ClientWSSecurityUsernameTokenHandler
clientWSSSecurityUsernameTokenHandler =
new ClientWSSecurityUsernameTokenHandler(soapClientProperties.getUsername(),
soapClientProperties.getPassword());
List<Handler> handlerChain = List.of(clientWSSSecurityUsernameTokenHandler);
((BindingProvider) serviceSOAP).getBinding().setHandlerChain(handlerChain);

源代码2-将ClientWSSecurityUsernameTokenHandler添加到SOAP处理程序链

下面的代码显示了如何将wss4j-ws安全性dom添加为依赖项。

<dependency>
<groupId>org.apache.wss4j</groupId>
<artifactId>wss4j-ws-security-dom</artifactId>
<version>2.4.1</version>
</dependency>

最新更新