1. 程式人生 > >jax-ws之webservice security(安全)教程第三天

jax-ws之webservice security(安全)教程第三天

前言:

在今天的學習中,我們講開始過渡到一個真正的websecurity例子。

第二天中我們知道了如何使用handler來處理客戶端提交上來的使用者名稱與密碼,而在今天的學習中,我們將會使用服務端預先配置的使用者名稱與密碼來authenticate客戶端提交上來的值。

相對於第二天的學習,如果客戶端提交的使用者名稱與密碼輸錯,但還是能夠與服務端建立http連線來說,第三天中的例子的安全性則更高,當客戶端提交上來的使用者名稱與密碼錯誤則更本不可能和服務端建立起有效的http連線。該例子同時適用於一切J2EE AppServer,比如說:IBMWAS, ORACLE WEBLOGIC。

同時,通過該例子將講述ws-security與相關的ws-policy進而一步步過渡到QoS。

1.1 配置J2EE AppServer中的相關使用者名稱與密碼

開啟tomcat下的cnof/tomcat-user.xml檔案:

<?xml version='1.0' encoding='utf-8'?>

<tomcat-users>

  <role rolename="operator"/>

  <user username="tomcatws" password="123456" roles="operator"/>

</tomcat-users>

通過上述配置,我們可以知道我們在tomcat中增加了一個角色叫”operator”,同時配置了一個使用者叫”tomcatws”密碼為”123456”,該使用者屬於operator角色。

1.2 配置web應用中的相關安全形色

請開啟你工程的web.xml檔案,加入下述這段內容:

<security-role>

                   <description>Normal operator user</description>

                   <role-name>operator</role-name>

</security-role>

<security-constraint>

                   <web-resource-collection>

                            <web-resource-name>Operator Roles Security</web-resource-name>

                            <url-pattern>/AuthHelloService</url-pattern>

                   </web-resource-collection>

                   <auth-constraint>

                            <role-name>operator</role-name>

                   </auth-constraint>

                   <user-data-constraint>

                            <transport-guarantee>NONE</transport-guarantee>

                   </user-data-constraint>

</security-constraint>

<login-config>

                   <auth-method>BASIC</auth-method>

</login-config>

這邊可以看到,我們把一個WebService的訪問置於了web security的保護下,如果需要該問該web資源,服務端需要驗證兩部分內容:

1)      是否是合法group/role中的使用者

2)      由於<auth-method>設定為basic,即客戶端要訪問相關的web資源時還需要提供使用者名稱與密碼

二、 開發我們的webservice

2.1 介面

package ctsjavacoe.ws.fromjava;

import javax.jws.WebMethod;

import javax.jws.WebService;

import javax.jws.soap.SOAPBinding;

import javax.jws.soap.SOAPBinding.Style;

@WebService

@SOAPBinding(style = Style.RPC)

public interface AuthHello {

         @WebMethod

         public String say(String name);

}

2.2 實現

package ctsjavacoe.ws.fromjava;

import javax.jws.WebService;

@WebService(endpointInterface = "ctsjavacoe.ws.fromjava.AuthHello")

public class AuthHelloImpl implements AuthHello {

         @Override

         public String say(String name) {

                   return "hello: " + name;

         }

}

該Web Service沒有任何特殊的地方,也沒有使用任何的handler,一切都交給了j2ee App容器去做認證

1.4 佈署webservice

看看我們得到了什麼:

OK,我們輸入tomcatws,密碼為123456

然後我們就得到正確的wsdl的輸出了,接下來我們用客戶端去連服務端。

package ctsjavacoe.ws.fromjava;

import javax.xml.namespace.QName;

import javax.xml.ws.Response;

import javax.xml.ws.BindingProvider;

import javax.xml.ws.Service;

import java.net.URL;

public class AuthHelloClient {

         private static final String WS_URL =

"file:D://wspace/JaxWSClient/wsdl/AuthHelloImplService.wsdl";

         private static final String S_URL =

"http://localhost:8080/JaxWSSample/AuthHelloService?wsdl";

         public static void main(String[] args) throws Exception {

                   URL url = new URL(WS_URL);

                   QName qname = new QName("http://fromjava.ws.ctsjavacoe/",

                                     "AuthHelloImplService");

                   Service service = Service.create(url, qname);

                   AuthHello port = service.getPort(AuthHello.class);

                   BindingProvider bp = (BindingProvider) port;

                   bp.getRequestContext().put(

BindingProvider.USERNAME_PROPERTY,

                                   "tomcatws");

                   bp.getRequestContext().put(

BindingProvider.PASSWORD_PROPERTY, "123456");

                   bp.getRequestContext().put(

BindingProvider.ENDPOINT_ADDRESS_PROPERTY,

                                   S_URL);

                   String rtnMessage = port.say("MK");

                   System.out.println("rtnMessage=====" + rtnMessage);

         }

}

關鍵語句我已經用紅色標粗。

要點:

1)      根據wsdl create出來一個Service,這邊需要一個wsdl,我們不可能用http://這樣形式的wsdl,因為我們此時沒有使用者名稱和密碼,如果我們使用的是http://這樣形式的wsdl直接會拋“授權認證出錯”,因此我們使用jax-ws在編譯服務端時生成的本地wsdl

2)      使用BindingProvider輸入使用者名稱與密碼,最後再使用BindingProvider輸入真正的我們服務端的wsdl即:http://localhost:8080/JaxWSSample/AuthHelloService?wsdl

下面來看執行效果:

故意輸錯使用者名稱與密碼,我們輸入:

bp.getRequestContext().put(

BindingProvider.USERNAME_PROPERTY,

                               "tomcatws");

bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "12345");

再來執行一下:

可以看到,如果使用者名稱密碼沒有輸對,根本無法通過認證,即連http連線都無法正確建立,這樣我們的安全程度極大的提高了。

4.1 加裝SSL通訊

即實現服務端與客戶端的HTTPS通訊,這一般可以保證傳輸過程中你的soap報文不會被攔截

4.2 報文加密

我們的使用者名稱與密碼是嵌在soapheader中的,是以明文方式存在的,如果一旦被推截,將造成災難性的結果,因此我們需要將我們的soap報文中header部分進行加密,這就是ws-security。

且慢些加密我們的soap-header,要加密很簡單,直接使用對稱或者非對稱演算法把使用者名稱密碼加個密然後傳輸至服務端解密不就完了。

是,是可以這麼做,但你有沒有想過,如果你的客戶端使用的是Java,服務端使用的是.net或者php怎麼辦?對方能用java api來解密嗎?

這時,我們就要使用wcf了,一起來看,什麼叫wcf.

Industry Standard WS-Security

Sun Microsystems and Microsoft jointly test Metro against WCF toensure that Sun web service clients (consumers) and web services (producers) doin fact interoperate with WCF web services applications and vice versa. Thistesting ensures that the following interoperability goals are realized:

l  Metro web services clients can access and consume WCF web services.

l  WCF web services clients can access and consume Metro web services.

Sun provides Metro on the Java platform and Microsoft provides WCFon the .NET 3.0 and .NET 3.5 platforms. The sections that follow describe theweb services specifications implemented by Sun Microsystems in Web ServicesInteroperability Technologies (WSIT) and provide high-level descriptions of howeach WSIT technology works.

這邊出現了一個名詞叫:Metro,這是一個基於JAXWS實現ws-security的標準框架,而Metro支援wcf協議。

現在知道我為什麼讓大家用jax-ws的意圖了吧。

Metro支援的ws-security有以下幾種:

ü  Username Authentication with Symmetric Key

ü  Username Authentication with Password Derived Keys

ü  Mutual Certificates Security

ü  Symmetric Binding with Kerberos Tokens

ü  Transport Security (SSL)

ü  Message Authentication over SSL

ü  SAML Authorization over SSL

ü  Endorsing Certificate

ü  SAML Sender Vouches with Certificates

ü  SAML Holder of Key

ü  STS Issued Token

ü  STS Issued Token with Service Certificate

ü  STS Issued Endorsing Token

真夠多的啊,我們看第一種,就是我們說的基於使用者名稱密碼的ws-security,而且這個soap報文中的使用者名稱與密碼是被加密的。

要寫符合WCF的webservice需要在webservice中引入QoS概念。

什麼是QoS,從字面上理解就是qualityof service,它是一個很廣的概念,它主要是把傳統的一個webservice從架構上再分成7個部分,即你的webservice需要包含下面7個主要的方面:

ü  Availability

ü  Performance

ü  Reliability

ü  Regulatory

ü  Security

ü  Integrity

ü  Accessibility

QoS所處的位置:

看到這邊大家頭不要大,我們一起來看,到底怎麼來實現QoS呢?

5.4 使用QoS中的security

我們使用QoS中的security來實現我們的soap報文的加密與傳輸,下面給出一個soap報文片段:

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

<soapenv:Envelope xmlns:soapenv="..." xmlns:wsa="...">

<soapenv:Header>

<wsse:Security xmlns:wsse="..." soapenv:mustUnderstand="1">

<wsse:BinarySecurityToken xmlns:wsu=...>MIIBvT...BnesE0=</wsse:BinarySecurityToken>

<EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">

<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>

<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">

<wsse:SecurityTokenReference>

<wsse:KeyIdentifier EncodingType="..." ValueType="...">hS6nfYE9axFgay+gorMEo0I4GfY=

</wsse:KeyIdentifier>

</wsse:SecurityTokenReference>

</ds:KeyInfo>

<CipherData>

<CipherValue>OULe5mAxLwYibommo1Ui/...1gvtagYQ=</CipherValue>

</CipherData>


如何生成上面這個soap報文的呢?

此時,你的webservice需要引入一個policy描述,即ws-policy,下面給出一個policy的片段:

    <wsp:Policy wsu:Id="HelloPortBindingPolicy">

        <wsp:ExactlyOne>

            <wsp:All>

                <wsam:Addressing wsp:Optional="false"/>

                <sp:SymmetricBinding>

                    <wsp:Policy>

                        <sp:ProtectionToken>

                            <wsp:Policy>

                                <sp:X509Token sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never">

                                    <wsp:Policy>

                                        <sp:WssX509V3Token10/>

                                        <sp:RequireIssuerSerialReference/>

                                    </wsp:Policy>

                                </sp:X509Token>

                            </wsp:Policy>

                        </sp:ProtectionToken>

                        <sp:Layout>

                            <wsp:Policy>

                                <sp:Strict/>

                            </wsp:Policy>

                        </sp:Layout>

                        <sp:IncludeTimestamp/>

                        <sp:OnlySignEntireHeadersAndBody/>

                        <sp:AlgorithmSuite>

                            <wsp:Policy>

                                <sp:Basic128/>

                            </wsp:Policy>

                        </sp:AlgorithmSuite>

即,在佈署webservice時,需要把空上policy和wsdl一起編譯成帶有QoS的webservice。

5.6 後話

在這個教程中,我不會帶出從頭至尾如何生成ws-policy和相應的帶有QoS的Webservice以及相關的客戶端。

因為大家如果把jax-ws的前五天基礎教程看完後,加上我這後三天的教程,完全可以自己有能力去用Metro來自己實現相關的soap報文加密。

這屬於非常easy的事,搞個2-3天就能實現。

同時,一般的專案,能夠真正用到通過soap報文傳遞使用者名稱與密碼或者通過handler的,並不多,一般都是用http://login.do?username=xxx&pwd=xxx這種拍屁股的做法在傳使用者名稱與密碼,頂多加個https了不得了。

如果當你碰到真正做到了帶有QoS的webservice時,自己結合我這8天教程,自己搞一下Metro就能搞得定,實在不行了,再來找我。

最後,給大家推薦3樣東西:

1.      Tomcat6扔了吧,用7了

2.      下載netbean7.0.1(其中自帶tomcat7)

3.      使用glassfish3(支援J2EE6規範),一個縮小免費版的weblogic

如果你對QoS有興趣,使用上述3樣東西會簡化你的學習過程。