天天看點

從 iOS,Objective-C, IPhone, iPad, Android, Java, Node.js 或其他平台,或通過其他開發語言,使用WS-Trust請求一個ADFS的令牌請求一個安全令牌對請求安全令牌的響應安全特性:結論:

(原文:http://leandrob.com/2012/02/request-a-token-from-adfs-using-ws-trust-from-ios-objective-c-iphone-ipad-android-java-node-js-or-any-platform-or-language/)

這不是一個為了SEO而使用的友好名稱。在這個文章中,我希望能向你們展示一個非常簡單的方法,無論你的平台或者開發語言是什麼,通過這個方法都可以在你的應用中提供Active Directory的認證,唯一的要求是你可以使用HTTP的POST方法。

請求一個安全令牌

談及ADFS,我們必定會說到 WS-Trust協定,在.NET平台上,感謝WCF和Windows Identity Foundation架構,這件事變得非常簡單;但是在平台無關的情況下,WS-Trust也沒有那麼難。

我們首先要知道的是,WS-Trust協定 定義了一個标準的方法來擷取安全令牌,這個方法基于一個XML結構,叫做Request Security Token 或者叫 RST,以下是這種結構的一個例子:

<trust:RequestSecurityTokenxmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
  <wsp:AppliesToxmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
   <a:EndpointReference>
     <a:Address>https://yourcompany.com</a:Address>
   </a:EndpointReference>
 </wsp:AppliesTo>
 <trust:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer</trust:KeyType>
 <trust:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</trust:RequestType>
 <trust:TokenType>urn:oasis:names:tc:SAML:2.0:assertion</trust:TokenType>
</trust:RequestSecurityToken>
           

關注本文的基本要素,上面這段XML中有幾個域是非常重要的。在RequestSecurityToken 元素中,你可以找到一個AppliesTo 的标記,在這裡,使用WS-Addressing标準,我們定義了令牌的有效範圍。在這個例子中,是:https://yourcompany.com.

RequestType 指定了你需要進行的操作,在本例中,Issue,這裡的意思是希望安全令牌服務((Security Token Service,或簡稱 STS)提供一個新的令牌;另一個RequestType 的選項是Renew,這個選項讓STS服務重新整理一個已有的已經存在的令牌。

最後, TokenType 指定了你所需要的令牌的類型。在我們的例子中,我們申請了一個基于SAML 2.0 格式的令牌。

看上去不是很難,是吧。但是我們在哪裡說我們是誰呢?好吧,一個更具體的,增加了一點複雜性的情況是,所有的WS-*協定棧的都是用SOAP協定上建構的,是以為了發送令牌請求,我們需要講一講SOAP。同樣的,SOAP也不是很難,SOAP也是基于XML的。我不打算解釋SOAP協定了,大家如果有興趣,可以在 http://www.w3.org/2003/05/soap-envelope/找到SOAP的格式說明。

在我們的例子中,要通過一個本地用戶端與ADFS通訊,我們需要提供針對使用者名與密碼(加密的),是以包裝好的SOAP消息就像下面看到的那樣:(為了更好的表達我要說明的問題,我裁剪了部分的參數)

<s:Envelopexmlns:s="http://www.w3.org/2003/05/soap-envelope"
            xmlns:a="http://www.w3.org/2005/08/addressing"
           xmlns:u="...">
  <s:Header>
    <a:Actions:mustUnderstand="1">http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue</a:Action>
<a:To s:mustUnderstand="1">
https://yourcompany.com/adfs/services/trust/13/UsernameMixed
</a:To>
    <o:Securitys:mustUnderstand="1" mlns:o="...">
     <o:UsernameTokenu:Id="uuid-6a13a244-dac6-42c1-84c5-cbb345b0c4c4-1">
       <o:Username>Leandro Boffi</o:Username>
        <o:PasswordType="...">[email protected]!</o:Password>
     </o:UsernameToken>
   </o:Security>
  </s:Header>
  <s:Body>
<trust:RequestSecurityToken
xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
     <wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
       <a:EndpointReference>
         <a:Address>https://yourcompany.com</a:Address>
       </a:EndpointReference>
     </wsp:AppliesTo>
     <trust:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer</trust:KeyType>
     <trust:RequestType>
http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue
</trust:RequestType>
     <trust:TokenType>urn:oasis:names:tc:SAML:2.0:assertion</trust:TokenType>
    </trust:RequestSecurityToken>
  </s:Body>
</s:Envelope>
           

為了快速了解這些格式,SOAP包有兩個主要的屬性:Head和Body。我們的消息的Body部分包含了 RST (Request for Security Token)消息,就像我們在上文建立的那樣。在Head的部分,我們會發現上下文的參數,例如服務端的Uri,在那個端點可以直接執行的Action,注意在SOAP中,在一個端點可以有多個Action,以及我們是誰 (加密的),在本例中就是使用者名和密碼。

為了使用使用者名和密碼認證,我們需要在提供的服務端找到一個Action,例如:https://yourcompany.com/adfs/services/trust/13/UsernameMixed, 是以我們需要確定ADFS已經配置。

一旦我們有了SOAP消息,我們隻要用普通的HTTP的POST方法發送到伺服器上。以下是在DOTNET的做法,但是類似的操作可以在其他平台上進行;或者通過其他的開發語言:

var client = new WebClient();
 
client.Headers.Add("Content-Type", "application/soap+xml; charset=utf-8");
 
var result = client.UploadString(
        address:"https://yourcompany.com/adfs/services/trust/13/UsernameMixed",
        method:"POST",
        data:soapMessage);
           

確定你的Html有中的Content-Type是 “application/soap+xml; charset=utf-8”,你最終發送到伺服器上的消息是像這樣的:

POST /adfs/services/trust/13/UsernameMixed HTTP/1.1
 
Connection: Keep-Alive
Content-Length: 1862
Content-Type: application/soap+xml; charset=utf-8
Accept-Encoding: gzip, deflate
Expect: 100-continue
Host: localhost
 
<s:Envelope ...> ... </s:Envelope>
           

為了和HTTP協定一緻,我增加了一些其他的标題。但是Content-Type是ADFS所必須的。

對請求安全令牌的響應

如果你的證書能夠通過驗證,而你的Uri也是範圍内規定的,那麼你會從ADFS收到一個SOAP結構的響應。這個響應的Body部分看上去像這樣:

<trust:RequestSecurityTokenResponseCollectionxmlns:trust="...">
 <trust:RequestSecurityTokenResponse>
   <trust:Lifetime>...</trust:Lifetime>
<wsp:AppliesToxmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
...
</wsp:AppliesTo>
   <trust:RequestedSecurityToken>
      <AssertionID="_fcf06a39-c495-4074-8f22-4a7df6e26513"
                IssueInstant="2012-02-21T04:27:24.771Z"
                Version="2.0"xmlns="urn:oasis:names:tc:SAML:2.0:assertion">
       <Issuer>http://yourcompany.com/adfs/services/trust</Issuer>
       <ds:Signaturexmlns:ds="http://www.w3.org/2000/09/xmldsig#">
         <ds:SignedInfo>
           <ds:CanonicalizationMethod
             Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
            <ds:SignatureMethod
             Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
           <ds:ReferenceURI="#_fcf06a39-c495-4074-8f22-4a7df6e26513">
             <ds:Transforms>
               <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
               <ds:TransformAlgorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
             </ds:Transforms>
             <ds:DigestMethodAlgorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
              <ds:DigestValue>...</ds:DigestValue>
           </ds:Reference>
         </ds:SignedInfo>
         <ds:SignatureValue>...</ds:SignatureValue>
         <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
           <ds:X509Data>
             <ds:X509Certificate>...</ds:X509Certificate>
           </ds:X509Data>
         </KeyInfo>
       </ds:Signature>
       <Subject>
         <SubjectConfirmationMethod="urn:oasis:names:tc:SAML:2.0:cm:bearer">
           <SubjectConfirmationData NotOnOrAfter="2012-02-21T04:32:24.771Z"/>
         </SubjectConfirmation>
       </Subject>
       <Conditions NotBefore="2012-02-21T04:27:24.756Z"NotOnOrAfter="2012-02-21T05:27:24.756Z">
         <AudienceRestriction>
           <Audience>https://yourcompany.com/</Audience>
         </AudienceRestriction>
       </Conditions>
       <AttributeStatement>
         <AttributeName="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name">
           <AttributeValue>Leandro Boffi</AttributeValue>
          </Attribute>
         <AttributeName="http://schemas.microsoft.com/ws/2008/06/identity/claims/role">
           <AttributeValue>Administrator</AttributeValue>
           <AttributeValue>Mobile User</AttributeValue>
         </Attribute>
        </AttributeStatement>
       <AuthnStatement AuthnInstant="2012-02-21T04:27:24.724Z">
         <AuthnContext>
           <AuthnContextClassRef>
             urn:oasis:names:tc:SAML:2.0:ac:classes:Password
           </AuthnContextClassRef>
          </AuthnContext>
       </AuthnStatement>
     </Assertion>
   </trust:RequestedSecurityToken>
   <trust:RequestedAttachedReference>...</trust:RequestedAttachedReference>
   <trust:RequestedUnattachedReference>...</trust:RequestedUnattachedReference>
    <trust:TokenType>urn:oasis:names:tc:SAML:2.0:assertion</trust:TokenType>
   <trust:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</trust:RequestType>
   <trust:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer</trust:KeyType>
 </trust:RequestSecurityTokenResponse>
</trust:RequestSecurityTokenResponseCollection>
           

這個格式在WS-Trust協定中也被稱為Request Security Token Response 或者RSTR,但是對你來說,在響應中最重要的内容在這裡:RequestSecurityTokenResponseCollection/RequestSecurityToeknResponse/RequestedSecurityToken.  這個标簽的内容是安全令牌,在我們的例子中,它是這樣的:

<AssertionID="_fcf06a39-c495-4074-8f22-4a7df6e26513"IssueInstant="2012-02-21T04:27:24.771Z"
                Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion">
 <Issuer>http://yourcompany.com/adfs/services/trust</Issuer>
  <ds:Signaturexmlns:ds="http://www.w3.org/2000/09/xmldsig#">
   <ds:SignedInfo>
     <ds:CanonicalizationMethodAlgorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
     <ds:SignatureMethodAlgorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
     <ds:ReferenceURI="#_fcf06a39-c495-4074-8f22-4a7df6e26513">
       <ds:Transforms>
         <ds:TransformAlgorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
         <ds:TransformAlgorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
       </ds:Transforms>
       <ds:DigestMethodAlgorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
        <ds:DigestValue>...</ds:DigestValue>
     </ds:Reference>
   </ds:SignedInfo>
   <ds:SignatureValue>...</ds:SignatureValue>
    <KeyInfoxmlns="http://www.w3.org/2000/09/xmldsig#">
     <ds:X509Data>
       <ds:X509Certificate>...</ds:X509Certificate>
     </ds:X509Data>
   </KeyInfo>
 </ds:Signature>
  <Subject>
   <SubjectConfirmationMethod="urn:oasis:names:tc:SAML:2.0:cm:bearer">
     <SubjectConfirmationDataNotOnOrAfter="2012-02-21T04:32:24.771Z"/>
   </SubjectConfirmation>
  </Subject>
  <ConditionsNotBefore="2012-02-21T04:27:24.756Z"NotOnOrAfter="2012-02-21T05:27:24.756Z">
   <AudienceRestriction>
     <Audience>https://yourcompany.com/</Audience>
   </AudienceRestriction>
 </Conditions>
 <AttributeStatement>
    <AttributeName="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name">
     <AttributeValue>Leandro Boffi</AttributeValue>
   </Attribute>
    <AttributeName="http://schemas.microsoft.com/ws/2008/06/identity/claims/role">
     <AttributeValue>Administrator</AttributeValue>
     <AttributeValue>Mobile User</AttributeValue>
   </Attribute>
 </AttributeStatement>
 <AuthnStatement AuthnInstant="2012-02-21T04:27:24.724Z">
   <AuthnContext>
     <AuthnContextClassRef>
       urn:oasis:names:tc:SAML:2.0:ac:classes:Password
     </AuthnContextClassRef>
   </AuthnContext>
 </AuthnStatement>
</Assertion>
           

一旦我們從響應内容中分解出了令牌,所有的一切都變得更簡單了:在AttributeStatment 節,你會得到一個Attribute的清單。這是申明,使用者的資訊,例如在這個令牌中我們有以下三個申明:

·        類型: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name

·        值: Leandro Boffi

·        類型: http://schemas.microsoft.com/ws/2008/06/identity/claims/role

·        值: Administrator

·        類型: http://schemas.microsoft.com/ws/2008/06/identity/claims/role

·        值: Mobile User

你可以使用這些申明來處理你應用裡的認證,但是同樣如果你的應用要調用一個依賴于你的ADFS的WebService的話。每個請求你都需要把整個令牌發送給WebService(我會在接下來的文章裡解釋這個情景)。

安全特性:

令牌有一些安全特性,使用這些特性,可以使我們的應用更加安全。我不打算在這個文章裡解釋所有這些特性,但是作為一個例子,如果我們希望驗證一下令牌自發出之後沒有人修改過它,或者說它是由它的發出方簽名的(在我們這個例子中,是ADFS),那麼你可以在 Assertion/Signature/SignatureValue找到一個簽名。這個簽名也是基于一個叫做XML簽名的标準的。你可以在下面的網站找到它的描述:http://www.w3.org/Signature/.

另一個非常重要的特性是,為了避免其他人利用舊的令牌,令牌隻有很多的生存時間。你可以在Assertion/Conditions/NotBefore 和NotOnOrAfter找到(令牌的生存時間)相關的資訊。

結論:

不論哪個平台或是哪種開發語言,把我們應用的身份認證內建到Active Directory是可以實作的,這是因為ADFS是基于WS-Trust這樣一個标準的協定。如果你的開發語言本身不支援WS-Trust,可能需要進行一些嘗試,但是如同我們所見的那樣,根本不是很難。你隻需要一個為SOAP+RST定制的XML模闆,并且可以調用一個HTTP POST就可以了。

這裡

這裡 可以下載下傳到一個模闆,用于調用SOAP-RST,隻要用你需要的值替換掉括号内的(值),并且開始請求令牌!

希望這能對你們有益!

繼續閱讀