Cookie是一個偉大的發明,它允許Web開發者保留他們的使用者的登入狀態。但是當你的網站有一個以上的網域時就會出現問題了。在Cookie規範上說,一個cookie只能用於一個域名,不能夠發給其它的域名。因此,如果在瀏覽器中對一個網域設定了一個cookie,這個cookie對於其它的網域將無效。如果你想讓你的用戶從你的網站中的其中一個進行登錄,同時也可以在其它網域上進行登錄,這可真是一個大難題。
跨二級域名
我們知道cookie是可以跨二級域名來訪問,這個很好理解,例如你www.test1.com 在的web應用程式創建了一個cookie,要想在bbs.test1.com這樣的二級域名對應的應用程式中訪問,就必須你在創建cookie的時候設定domain參數domain=test1.com。 以asp.net為例程式碼如下:
HttpCookie cookie = new HttpCookie("name", "www.Admin10000.com"); cookie.Domain = "test1.com"; cookie.Path = "/"; Response.Cookies.Add(cookie);
跨頂級域名
如果我不是二級域名而是完全在不同頂級域名中,例如www.test1.com 所在的web應用程式創建了一個cookie,想要在www.test2.com 或其二級網域的應用程式中訪問,改怎麼辦呢?我們知道靠常規反的方法是訪問不了的,關鍵我們就是看看有沒有方法可以訪問。事實是Cookie可以在一定條件下跨域,而不是隨心所欲的實現跨域。
我們來做個測試,看看兩個網站 www.test1.com 和 www.test2.com 如何實現cookie跨域存取。 按照常規我們需要有2個頂級域名,並且有DNS伺服器才能夠配置域名,否則我們是無法驗證的,但是這裡我們也沒有必要那麼麻煩,我們可以透過修改hosts檔案來模擬。在 c:windowssystem32driversetc 中有 hosts文件,在末尾添加上
127.0.0.1 www.test1.com 127.0.0.1 www.test2.com
兩行,就可以將本機用上面的域名訪問本機回環地址了。我們只需要在IIS上部署一套程序,ip為本機回環位址,用兩個網域分別存取就可以了。
我們新三個頁面,分別是 Default.aspx、SSO.ashx、GetCookie.aspx。
其中Default.aspx是 www.test1.com 的頁面,造訪的網址是 http://www.test1.com/Default.aspx。看看前台代碼,它沒有任何後台代碼
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Admin10000.Web.Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> <script type="text/javascript"> var _frm = document.createElement("iframe"); _frm.style.display = "none"; _frm.src = "http://www.test2.com/SSO.ashx"; document.body.appendChild(_frm); </script> </div> </form> </body> </html>
另外一個是SSO.ashx 頁面,我們認為它是www.test2.com 的頁面,前台沒有任何代碼,後台代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Services; using System.Web.SessionState; namespace Admin10000.Web { /// <summary> /// $codebehindclassname$ 的摘要说明 /// </summary> [WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] public class SSO : IHttpHandler { public void ProcessRequest(HttpContext context) { HttpCookie cookie = new HttpCookie("name", "www.Admin10000.com"); cookie.Domain = "test2.com"; cookie.Path = "/"; cookie.Expires = DateTime.Now.AddMinutes(10000); context.Response.Cookies.Add(cookie); context.Response.ContentType = "text/plain"; context.Response.AddHeader("P3P", "CP=CAO PSA OUR"); context.Response.Write(""); } public bool IsReusable { get { return false; } } } }
最後是GetCookie.aspx頁面,它同樣是www.test2.com下的頁面,沒有前台代碼,只有後台代碼:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace Admin10000.Web { public partial class GetCookie : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (Request.Cookies["name"] != null) { Response.Write(Request.Cookies["name"].Value); } } } }
好了,現在我們訪問測試,通過訪問http://www.test1.com/Default.aspx之後,此時會透過iframe載入呼叫SSO.ashx這個頁面,執行後台程式碼建立cookie,然後造訪http://www.test2.com/GetCookie.aspx 我們得到了對應的cookie。說明在www.test1.com下建立的cookie在www.test2.com下是可以存取的。
要注意的地方:
admin10000.com 提示 SSO.ashx 的後台程式碼中有一句:context.Response.AddHeader("P3P", "CP=CAO PSA OUR"); 是用來設定P3P3P3P3P3P3);是因為IE瀏覽器支援的P3P導致iframe跨站點時cookie被阻止,無法建立cookie。 (FireFox目前仍不支援P3P安全特性,FireFox自然也不存在此問題。不需要新增P3P回應頭。)
透過iframe的src屬性將test1.com域下的cookie值作為get參數重定向到test2.在com域下SSO.ashx頁面上,SSO.ashx取得test1.com域中所傳過來的cookie值,並將所獲取到值寫入cookie中,這樣就簡單的實現了cookie跨域的存取。
另外Default.aspx頁面也可改為JS呼叫形式:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Admin10000.Web.Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> <script type="text/javascript" src="http://www.test2.com/SSO.ashx"></script> </div> </form> </body> </html>