/ PHP

[php tech.] MSN Bot step by step part 1 SSO HOWTO

網路上有現成的 class 可以使用,請參閱:phpmsnclass V1.11,這是他的 Google Code。現成的類別很方便使用,所以在這裡就不多說關於類別的事情,我這邊從 protocol 開始,也許有人會覺得繁瑣,不過,就當作是我自己筆記用的好了,關於 MSN protocol 可以參考這裡:http://msnpiki.msnfanatic.com/index.php/Main_Page

首先,我們先從 SSO(Single Sign-On) 說起。我們利用 PHP 的 Curl 函式,將我們所要做的認證送給 MSN 伺服器。

關於 SSO 所需要傳送的 XML 規格,可以參考這裡:http://msnpiki.msnfanatic.com/index.php/MSNP15:SSO 由 SSO 的狀況我們可以知道,總共有 RST0 ~ RST7 等八種狀態的 Ticket Token,分別是:

  1. RST0:Passport.net 認證。
  2. RST1:Messenger Live 認證。
  3. RST2:MSN Messenger 認證。
  4. RST3:MSN 聯絡人清單認證。
  5. RST4:Messenger Live OIM 離線訊息認證。
  6. RST5:Live Spaces 認證。
  7. RST6:Live 聯絡人清單認證。
  8. RST7:Live Storage 認證。

以上這個順序是依照 SSO 的清單所列出來的,這些順序資料跟現成的 Class 並不太相同,當然,順序是可以自己修改的,所以也不一定是這種序列,這裡只是提出來說明,關於 SSO 可以傳送並取得的 token 有這些。至於傳送的方式,我用第一個 Passport.net 認證舉例,他是這麼寫的:

<?php
    $userid = ''; // 你的帳號
    $password = ''; // 你的密碼

    $XML = '<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"
          xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext"
          xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"
          xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy"
          xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
          xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"
          xmlns:wssc="http://schemas.xmlsoap.org/ws/2004/04/sc"
          xmlns:wst="http://schemas.xmlsoap.org/ws/2004/04/trust">
<Header>
  <ps:AuthInfo xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="PPAuthInfo">
    <ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>
    <ps:BinaryVersion>4</ps:BinaryVersion>
    <ps:UIVersion>1</ps:UIVersion>
    <ps:Cookies></ps:Cookies>
    <ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>
  </ps:AuthInfo>
  <wsse:Security>
    <wsse:UsernameToken Id="user">
      <wsse:Username>'.$userid.'</wsse:Username>
      <wsse:Password>'.$password.'</wsse:Password>
    </wsse:UsernameToken>
  </wsse:Security>
</Header>
<Body>
  <ps:RequestMultipleSecurityTokens xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="RSTS">
    <wst:RequestSecurityToken Id="RST0">
      <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
      <wsp:AppliesTo>
        <wsa:EndpointReference>
          <wsa:Address>http://Passport.NET/tb</wsa:Address>
        </wsa:EndpointReference>
      </wsp:AppliesTo>
    </wst:RequestSecurityToken>
  </ps:RequestMultipleSecurityTokens>
</Body>
</Envelope>';
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, 'https://login.live.com/RST.srf');
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt($curl, CURLOPT_POST, 1);
    curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
    $data = curl_exec($curl);
    curl_close($curl);
    $fp = fopen('./test.log', 'a+');
    if ($fp) {
        fputs($fp, $data);
        fclose($fp);
    }

直接運行之後,他會產生一個叫做 test.log 的檔案,那個檔案就是 MSN 伺服器所返回的資料。至於他會返回些甚麼呢?我這邊舉兩個例子,一個是返回認證失敗的內容,另一個則是返回認證成功的內容。

<?xml version="1.0" encoding="utf-8" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:psf="http://schemas.microsoft.com/Passport/SoapServices/SOAPFault">
<S:Header>
<psf:pp xmlns:psf="http://schemas.microsoft.com/Passport/SoapServices/SOAPFault">
<psf:serverVersion>1</psf:serverVersion>
<psf:authstate>0x80048800</psf:authstate>
<psf:reqstatus>0x80048820</psf:reqstatus>
<psf:serverInfo Path="Live1" RollingUpgradeState="ExclusiveNew" LocVersion="0" ServerTime="2009-07-02T02:28:07Z">BAYIDSLGN1K12 2009.05.08.01.28.02</psf:serverInfo>
<psf:cookies/>
<psf:response/>
</psf:pp>
</S:Header>
<S:Fault>
<faultcode>S:Client</faultcode>
<faultstring>Invalid Request</faultstring>
</S:Fault>
</S:Envelope>

這是返回失敗的例子,注意到他有 <faultcode> 的返回項目,代表著你傳送的資料有錯誤。

<?xml version="1.0" encoding="utf-8" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Header>
<psf:pp xmlns:psf="http://schemas.microsoft.com/Passport/SoapServices/SOAPFault">
<psf:serverVersion>1</psf:serverVersion>
<psf:PUID>0003BFFD93BB0005</psf:PUID>
<psf:configVersion>6.0.11409.0</psf:configVersion>
<psf:uiVersion>3.100.2179.0</psf:uiVersion>
<psf:authstate>0x48803</psf:authstate>
<psf:reqstatus>0x0</psf:reqstatus>
<psf:serverInfo Path="Live1" RollingUpgradeState="ExclusiveNew" LocVersion="0" ServerTime="2009-07-02T02:28:51Z">BAYIDSLGN1S18 2009.05.08.01.28.02</psf:serverInfo>
<psf:cookies/>
<psf:browserCookies>
<psf:browserCookie Name="MH" URL="http://www.msn.com">MH=MSFT; path=/; domain=.msn.com; expires=Wed, 30-Dec-2037 16:00:00 GMT</psf:browserCookie>
<psf:browserCookie Name="MHW" URL="http://www.msn.com">MHW=; path=/; domain=.msn.com; expires=Thu, 30-Oct-1980 16:00:00 GMT</psf:browserCookie>
<psf:browserCookie Name="MH" URL="http://www.live.com">MH=MSFT; path=/; domain=.live.com; expires=Wed, 30-Dec-2037 16:00:00 GMT</psf:browserCookie>
<psf:browserCookie Name="MHW" URL="http://www.live.com">MHW=; path=/; domain=.live.com; expires=Thu, 30-Oct-1980 16:00:00 GMT</psf:browserCookie>
</psf:browserCookies>
<psf:credProperties>
<psf:credProperty Name="MainBrandID">MSFT</psf:credProperty>
<psf:credProperty Name="BrandIDList">
</psf:credProperty>
<psf:credProperty Name="IsWinLiveUser">false</psf:credProperty>
<psf:credProperty Name="CID">e75f7fe32aea2838</psf:credProperty>
<psf:credProperty Name="AuthMembername">[email protected]</psf:credProperty>
</psf:credProperties>
<psf:extProperties>
<psf:extProperty Name="ANON" Expiry="Mon, 18-Jan-2010 10:28:51 GMT" Domains="bing.com;atdmt.com" IgnoreRememberMe="false">A=AC09B2815155B5C33BA3E160FFFFFFFF&amp;E=88d&amp;W=1</psf:extProperty>
<psf:extProperty Name="NAP" Expiry="Sat, 10-Oct-2009 09:28:51 GMT" Domains="bing.com;atdmt.com" IgnoreRememberMe="false">V=1.9&amp;E=833&amp;C=iruWWhJxoXnSMAY9Xbla_KEevr9BKN2iBGAXwaBzpLkBfOohtiiAYg&amp;W=1</psf:extProperty>
<psf:extProperty Name="LastUsedCredType">1</psf:extProperty>
</psf:extProperties>
<psf:response/>
</psf:pp>
</S:Header>
<S:Body>
<wst:RequestSecurityTokenResponseCollection xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wst="http://schemas.xmlsoap.org/ws/2004/04/trust" xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion" xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy" xmlns:psf="http://schemas.microsoft.com/Passport/SoapServices/SOAPFault">
<wst:RequestSecurityTokenResponse>
<wst:TokenType>urn:passport:legacy</wst:TokenType>
<wsp:AppliesTo xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing">
<wsa:EndpointReference>
<wsa:Address>http://Passport.NET/tb</wsa:Address>
</wsa:EndpointReference>
</wsp:AppliesTo>
<wst:LifeTime>
<wsu:Created>2009-07-02T02:28:51Z</wsu:Created>
<wsu:Expires>2009-07-03T02:28:51Z</wsu:Expires>
</wst:LifeTime>
<wst:RequestedSecurityToken>
<EncryptedData xmlns="http://www.w3.org/2001/04/xmlenc#" Id="BinaryDAToken0" Type="http://www.w3.org/2001/04/xmlenc#Element">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc">
</EncryptionMethod>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:KeyName>http://Passport.NET/STS</ds:KeyName>
</ds:KeyInfo>
<CipherData>
<CipherValue>ATcwZCPTlRTD4abiB7sCiS/9oQTlN+Zd3Z1X/Umfr42gJ6PPSiDP7CBqvDMIB+rfUeENFM2ksp03CojxKV2HH1UrLpqhA1D5rgnEFhNZWQxRpoWlFUo21FMzP2j01w12aFPrvvIJrex7E+BKdWoKEymOeGctKYS0xf6bd6JzCDvtBtEeUd5GQatYCRXrhRU7214/udW0yw+y+Mx6VnRdAYoPAm+jap8HsMbTEQB7u3X6Odcz2wuZB6/8GByvbqLaOwR0jcYNyOiMvUeXZYa6A3b19xFHfQGsNYARVuYkSp748NVwMloBsnWElRW3ynvujzWMTeBPRsCxW+oYCfrCrslR+t9d/Di8vmW5AbAnmLQni0reLW/qmPZwY8u5FuvWmIedIUA7Hs7Yf3Z6dNOHjypPBl5Ml7AJxjCqDbZraUqEaUMiemjw+JQ/W/4JpMsZ3pPhYcQDJRv9sjDaZFcFwQbZYmnoCKebpmiZetkJthAmaTahOezHBl13NHsz</CipherValue>
</CipherData>
</EncryptedData>
</wst:RequestedSecurityToken>
<wst:RequestedTokenReference>
<wsse:KeyIdentifier ValueType="urn:passport">
</wsse:KeyIdentifier>
<wsse:Reference URI="#BinaryDAToken0">
</wsse:Reference>
</wst:RequestedTokenReference>
<wst:RequestedProofToken>
<wst:BinarySecret>mnJ+1QLiTC4SLDgLZI5bz+J6qZbwTBlr</wst:BinarySecret>
</wst:RequestedProofToken>
</wst:RequestSecurityTokenResponse>
</wst:RequestSecurityTokenResponseCollection>
</S:Body>
</S:Envelope>

這是返回成功的例子,留意,它返回了 wst:BinarySecret> 的部分,就是我們所需要的 Token 資料。另外,並不是每一種返回都會是 <wst:BinarySecret>,而是 Passport 比較特別,其餘的 Protocol 都是返回 <wsse:BinarySecurityToken ...> 這種形式,所以在取用上會略有不同。

所以當我們依照上述的順序來傳送所有的資料,並取回 token 的時候,利用正規表示式將這些 Token 分別取出來,那麼他們的順序如下:

  1. Passport.net / MSN Messanger 認證。
  2. Passport.net / MSN Messanger 認證(Binary Secret)。
  3. MSN Messanger 認證。
  4. Messanger 聯絡人認證。
  5. MSN Messanger OIM 離線訊息認證。
  6. Live Spaces 認證。
  7. Live 聯絡人清單認證。
  8. Live Storage 認證。

以上,是對於 SSO 的簡單介紹。下一篇我將簡單介紹一下如何與 MSN 伺服器做連線溝通。

附註:還有更強大的 .net 套件,請拜:http://code.google.com/p/msnp-sharp/