JAX-WS使用Handler實現簡單的WebService許可權驗證
阿新 • • 發佈:2019-01-07
WebService如果涉及到安全保密或者使用許可權的時候,WS-Security通常是最優選擇。WS-Security (Web服務安全) 包含了關於如何在WebService訊息上保證完整性和機密性的規約,如何將簽名和加密頭加入SOAP訊息。不過WS-Security也有一些效能上的損耗,在資訊保密要求不是很高的情況下,可以通過在SOAPHeader中新增簡單的校驗資訊實現。
具體思路是客戶端呼叫需要認證的服務時,在SOAPHeader中新增授權資訊(如使用者名稱、密碼或者序列號等)。服務端收到請求,在SOAPHeader中校驗授權資訊,校驗通過則執行請求,校驗不通過則返回錯誤提示。
客戶端發起請求在SOAPHeader中新增的授權資料格式如下
<auth xmlns="http://www.tmp.com/auth">
<name>admin</name>
<password>admin</password>
</auth>
服務端
服務端授權校驗Handler
import java.util.Iterator; import java.util.Set; import javax.xml.namespace.QName; import javax.xml.soap.SOAPBody; import javax.xml.soap.SOAPElement; import javax.xml.soap.SOAPEnvelope; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPFault; import javax.xml.soap.SOAPHeader; import javax.xml.soap.SOAPMessage; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.handler.soap.SOAPHandler; import javax.xml.ws.handler.soap.SOAPMessageContext; /** * 服務端請求校驗Handler * @author
[email protected] * */ public class ValidateAuthHandler implements SOAPHandler<SOAPMessageContext> { @Override public void close(MessageContext context) { } @Override public boolean handleFault(SOAPMessageContext context) { return true; } @Override public boolean handleMessage(SOAPMessageContext context) { // 判斷訊息是請求還是響應 Boolean output = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); boolean result = false; SOAPMessage message = context.getMessage(); //如果是請求,則執行校驗 if(!output){ result = validate(message); if(!result){ validateFail(message); } } System.out.println(output ? "服務端響應:" : "服務端接收:"); try { message.writeTo(System.out); } catch (Exception e) { e.printStackTrace(); } System.out.println("\r\n"); return result; } /** * 授權校驗失敗,在SOAPBody中新增SOAPFault * @param message */ private void validateFail(SOAPMessage message) { try { SOAPEnvelope envelop = message.getSOAPPart().getEnvelope(); envelop.getHeader().detachNode(); envelop.addHeader(); envelop.getBody().detachNode(); SOAPBody body = envelop.addBody(); SOAPFault fault = body.getFault(); if (fault == null) { fault = body.addFault(); } fault.setFaultString("授權校驗失敗!"); message.saveChanges(); } catch (SOAPException e) { e.printStackTrace(); } } /** * 授權校驗 * @param message * @return 校驗成功返回true,校驗失敗返回false */ private boolean validate(SOAPMessage message){ boolean result = false; try { SOAPEnvelope envelop = message.getSOAPPart().getEnvelope(); SOAPHeader header = envelop.getHeader(); if(header != null){ Iterator iterator = header.getChildElements(new QName("http://www.tmp.com/auth", "auth")); SOAPElement auth = null; if(iterator.hasNext()){ //獲取auth auth = (SOAPElement)iterator.next(); //獲取name Iterator it = auth.getChildElements(new QName("http://www.tmp.com/auth", "name")); SOAPElement name = null; if(it.hasNext()){ name = (SOAPElement)it.next(); } //獲取password it = auth.getChildElements(new QName("http://www.tmp.com/auth", "password")); SOAPElement password = null; if(it.hasNext()){ password = (SOAPElement)it.next(); } //判斷name和password是否符合要求 if(name != null && password != null && "admin".equals(name.getValue()) && "admin".equals(password.getValue())){ result = true; } } } } catch (SOAPException e) { e.printStackTrace(); } return result; } @Override public Set<QName> getHeaders() { return null; } }
客戶端
客戶端新增授權Handler
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
/**
* 客戶端新增請求授權
* @author [email protected]
*
*/
public class AddAuthHandler implements SOAPHandler<SOAPMessageContext> {
@Override
public boolean handleMessage(SOAPMessageContext context) {
// 判斷訊息是請求還是響應
Boolean output = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
SOAPMessage message = context.getMessage();
if (output) {
try {
SOAPHeader header = message.getSOAPHeader();
if (header == null) {
header = message.getSOAPPart().getEnvelope().addHeader();
}
SOAPElement auth = header.addChildElement(new QName("http://www.tmp.com/auth", "auth"));
SOAPElement name = auth.addChildElement("name");
name.addTextNode("admin");
SOAPElement password = auth.addChildElement("password");
password.addTextNode("admin");
message.saveChanges();
} catch (SOAPException e) {
e.printStackTrace();
}
}
System.out.println(output ? "客戶端請求:" : "客戶端接收:");
try {
message.writeTo(System.out);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("\r\n");
return true;
}
@Override
public boolean handleFault(SOAPMessageContext context) {
return true;
}
@Override
public void close(MessageContext context) {
}
@Override
public Set<QName> getHeaders() {
return null;
}
}
客戶端Handler配置檔案handler-chain.xml
<?xml version="1.0" encoding="UTF-8"?>
<javaee:handler-chains xmlns:javaee="http://java.sun.com/xml/ns/javaee"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<javaee:handler-chain>
<javaee:handler>
<javaee:handler-class>com.rvho.client.handler.AddAuthHandler</javaee:handler-class>
</javaee:handler>
</javaee:handler-chain>
</javaee:handler-chains>
客戶端的Service中新增Handler配置檔案
/**
* This class was generated by the JAX-WS RI.
* JAX-WS RI 2.2.9-b130926.1035
* Generated source version: 2.2
*
*/
@WebServiceClient(name = "HelloWSService", targetNamespace = "http://www.tmp.com/ws/hello", wsdlLocation = "http://localhost:8014/jaxwsserver/services/hello?wsdl")
@HandlerChain(file="handler-chain.xml")
public class HelloWSService
extends Service
{
private final static URL HELLOWSSERVICE_WSDL_LOCATION;
private final static WebServiceException HELLOWSSERVICE_EXCEPTION;
private final static QName HELLOWSSERVICE_QNAME = new QName("http://www.tmp.com/ws/hello", "HelloWSService");
static {
URL url = null;
WebServiceException e = null;
try {
url = new URL("http://localhost:8014/jaxwsserver/services/hello?wsdl");
} catch (MalformedURLException ex) {
e = new WebServiceException(ex);
}
HELLOWSSERVICE_WSDL_LOCATION = url;
HELLOWSSERVICE_EXCEPTION = e;
}
public HelloWSService() {
super(__getWsdlLocation(), HELLOWSSERVICE_QNAME);
}
public HelloWSService(WebServiceFeature... features) {
super(__getWsdlLocation(), HELLOWSSERVICE_QNAME, features);
}
public HelloWSService(URL wsdlLocation) {
super(wsdlLocation, HELLOWSSERVICE_QNAME);
}
public HelloWSService(URL wsdlLocation, WebServiceFeature... features) {
super(wsdlLocation, HELLOWSSERVICE_QNAME, features);
}
public HelloWSService(URL wsdlLocation, QName serviceName) {
super(wsdlLocation, serviceName);
}
public HelloWSService(URL wsdlLocation, QName serviceName, WebServiceFeature... features) {
super(wsdlLocation, serviceName, features);
}
/**
*
* @return
* returns HelloWS
*/
@WebEndpoint(name = "HelloWSPort")
public HelloWS getHelloWSPort() {
return super.getPort(new QName("http://www.tmp.com/ws/hello", "HelloWSPort"), HelloWS.class);
}
/**
*
* @param features
* A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy. Supported features not in the <code>features</code> parameter will have their default values.
* @return
* returns HelloWS
*/
@WebEndpoint(name = "HelloWSPort")
public HelloWS getHelloWSPort(WebServiceFeature... features) {
return super.getPort(new QName("http://www.tmp.com/ws/hello", "HelloWSPort"), HelloWS.class, features);
}
private static URL __getWsdlLocation() {
if (HELLOWSSERVICE_EXCEPTION!= null) {
throw HELLOWSSERVICE_EXCEPTION;
}
return HELLOWSSERVICE_WSDL_LOCATION;
}
}
客戶端發起index請求URL wsdlUrl = new URL("http://localhost:7180/jaxwsserver/services/hello?wsdl");
HelloWSService helloWSS = new HelloWSService(wsdlUrl);
HelloWS helloWS = helloWSS.getHelloWSPort();
String index = helloWS.index();
客戶端發起正確授權的請求以及伺服器的響應
<!-- 客戶端請求 -->
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
<auth xmlns="http://www.tmp.com/auth">
<name>admin</name>
<password>admin</password>
</auth>
</SOAP-ENV:Header>
<S:Body>
<ns2:index xmlns:ns2="http://www.tmp.com/ws/hello" />
</S:Body>
</S:Envelope>
<!-- 服務端響應 -->
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<S:Body>
<ns2:indexResponse xmlns:ns2="http://www.tmp.com/ws/hello">
<return>hello</return>
</ns2:indexResponse>
</S:Body>
</S:Envelope>
客戶端發起錯誤授權的請求以及伺服器的響應
<!-- 客戶端請求 -->
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
<auth xmlns="http://www.tmp.com/auth">
<name></name>
<password></password>
</auth>
</SOAP-ENV:Header>
<S:Body>
<ns2:index xmlns:ns2="http://www.tmp.com/ws/hello" />
</S:Body>
</S:Envelope>
<!-- 伺服器響應 -->
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<S:Header/>
<S:Body>
<S:Fault>
<faultcode>S:Server</faultcode>
<faultstring>授權校驗失敗!</faultstring>
</S:Fault>
</S:Body>
</S:Envelope>
HandlerReolver代替Handler配置檔案
handler-chain配置檔案對所有的請求都新增授權驗證資訊,有些時候不是所有的請求都需要新增授權驗證,HandlerResolver提供了在程式設計時新增Handler的方法,可以用HandlerResolver給需要授權的介面新增Handler。
URL wsdlUrl = new URL("http://localhost:7180/jaxwsserver/services/hello?wsdl");
HelloWSService helloWSS = new HelloWSService(wsdlUrl);
//通過HandlerResolver新增Handler
helloWSS.setHandlerResolver(new HandlerResolver(){
@Override
@SuppressWarnings("rawtypes")
public List<Handler> getHandlerChain(PortInfo portInfo) {
List<Handler> handlerChain = new ArrayList<Handler>();
handlerChain.add(new AddAuthHandler());
return handlerChain;
}
});
HelloWS helloWS = helloWSS.getHelloWSPort();
//呼叫index服務
String index = helloWS.index();