跨二級域名
我們知道cookie可以跨二級域名來訪問,這個很好理解,例如你在www.cmj.com所在的web應用程序創建了一個cookie,在cs.cmj.com這樣的二級域名對應的應用程序中可以訪問,當然你在創建cookie的時候需要指出Domain屬性為cmj.com。
跨域名問題
現在問題就是如果我不是二級域名而是完全在不同的域中,例如現在是www.smm.com想要訪問www.cmj.com或其二級域名對應的程序中創建的cookie怎么辦呢?我們知道至少靠常規反的方法是訪問不了的,關鍵我們就是看看有沒有方法可以訪問。事實是Cookie可以在一定條件下跨域,而不是隨心所欲的實現跨域。
做個實驗
例如就像上面所說的有兩個站點分別是www.cmj.com和www.smm.com現在就來看看如何跨域。首先按照常規我們需要有DNS服務器才能夠配置域名,否則我們是無法驗證的(沒有域名怎么跨域啊O(∩_∩)O~),但是這里我們也沒有必要那么麻煩,我們可以通過修改hosts文件來模擬。在C:/Windows/System32/drivers/etc中有hosts文件,添加上
127.0.0.1 www.cmj.com
兩行,就可以將本機用上面的域名訪問本機回環地址了。當然,可以看出來我們只需要在iis上部署同一套程序,ip為本機回環地址,用兩個域名分別訪問就可以了。
那么看一下程序,我們有三個頁面,其中認為cmj_index.aspx是www.cmj.com的頁面,也就是說訪問的地址是http://www.cmj.com/cmj_index.aspx。看一下前台代碼,它沒有任何后台代碼:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="cmj_index.aspx.cs" Inherits="WebApplication1.cmj_index" %>
<!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>
<iframe src= " http://www.smm.com/smm_setCookie.aspx " mce_src= " http://www.smm.com/smm_setCookie.aspx "></iframe>
</div>
</form>
</body>
</html>
另外一個是smm_setCookie.aspx這個頁面,當然我們認為它是www.smm.com的頁面,前台沒有任何代碼,這是后台代碼:
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WebApplication1
{
public partial class smm_setCookie : System.Web.UI.Page
{
protected void Page_Load( object sender, EventArgs e)
{
HttpCookie cookie = new HttpCookie( " smmCookie ");
cookie.Domain = " smm.com ";
cookie.Expires = DateTime.Now.AddMinutes( 10);
cookie.Values.Add( " cookieInfo ", " 這里是cookie的值! ");
Response.AppendCookie(cookie);
Response.Write( " cookie已經成功創建! ");
}
}
}
最后就是smm_getCookie.aspx頁面了,它同樣是www.smm.com下的頁面,沒有前台代碼,只有后台代碼:
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WebApplication1
{
public partial class smm_getCookie : System.Web.UI.Page
{
protected void Page_Load( object sender, EventArgs e)
{
Response.Write(Request.Cookies[ " smmCookie "][ " cookieInfo "]);
}
}
}
我們現在要做的就是看看,當我們通過http://www.cmj.com/cmj_index.aspx之后(我們知道這時smm_setCookie頁面會執行后台代碼創建cookie),在http://www.smm.com/smm_getCookie.aspx中能否得到相應的cookie。如果能得到,說明在www.cmj.com下創建的cookie在www.smm.com下是可以訪問到的。
首先訪問一下http://www.cmj.com/cmj_index.aspx
很明顯是失敗的,一定條件下(其實就是將一個域中的設置cookie的操作放到frame中)的跨域也是失敗的。其實是條件還不夠,還需要在header中加入p3p。設置cookie的代碼前加入一行代碼:
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WebApplication1
{
public partial class smm_setCookie : System.Web.UI.Page
{
protected void Page_Load( object sender, EventArgs e)
{
Response.AddHeader( " P3P ", " CP=CAO PSA OUR ");
HttpCookie cookie = new HttpCookie( " smmCookie ");
cookie.Domain = " smm.com ";
cookie.Expires = DateTime.Now.AddMinutes( 10);
cookie.Values.Add( " cookieInfo ", " 這里是cookie的值! ");
Response.AppendCookie(cookie);
Response.Write( " cookie已經成功創建! ");
}
}
}
重復上面的訪問就會發現:cookie可以使用
具體這時為什么呢,其實因為P3P允許我們訪問第三方cookie造成的,當然之所以不加不能訪問是因為IE默認不支持訪問第三方cookie(當然通過配置隱私信息也可以支持),其實在Firefox和chrome瀏覽其中默認就是支持訪問第三方cookie的,當然也就不用加P3P的頭信息了。這里對於P3P不做過多介紹,網上很多。
我們其實已經看到了,cookie跨域的情況,那么這是不是說明創建cookie的時候只要加入相應的頭信息就可以在各種瀏覽器中隨心所欲跨域了呢?在上面的程序的基礎上添加一個頁面Default.aspx,前台代碼:
<!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>
<asp:Label ID= " Label1 " runat= " server " Text= " Cookie寫入值: "></asp:Label>
<asp:TextBox ID= " tbWriteValue " runat= " server "></asp:TextBox>
<asp:Button ID= " btnWrite " runat= " server " Text= " 寫入 " onclick= " btnWrite_Click " /><br />
<asp:Label ID= " Label2 " runat= " server " Text= " Cookie讀取值: "></asp:Label>
<asp:TextBox ID= " tbReadValue " runat= " server "></asp:TextBox>
<asp:Button ID= " btnRead " runat= " server " Text= " 讀取 " onclick= " btnRead_Click " /><br />
</div>
</form>
</body>
</html>
后台代碼(其實和上面的代碼完全一樣):using System;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WebApplication1
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load( object sender, EventArgs e)
{
}
protected void btnWrite_Click( object sender, EventArgs e)
{
Response.AddHeader( " P3P ", " CP=CAO PSA OUR ");
HttpCookie cookie = new HttpCookie( " smmCookie ");
cookie.Domain = " smm.com ";
cookie.Expires = DateTime.Now.AddMinutes( 10);
cookie.Values.Add( " cookieInfo ", " 這里是cookie的值! ");
Response.AppendCookie(cookie);
Response.Write( " cookie已經成功創建! ");
}
protected void btnRead_Click( object sender, EventArgs e)
{
Response.Write(Request.Cookies[ " smmCookie "][ " cookieInfo "]);
}
}
}
我們只需要看看在www.cmj.com中設置cookie,在www.smm.com中是否可以讀取就可以了。
在 www.smm.com 中讀取(肯定能讀取到,在同一個域中,說明程序沒錯O(∩_∩)O~)
在www.cmj.com中讀取
結論
好了,答案出來了。Cookie可以跨域,但是並不是只要加入頭信息就可以像想象中那樣跨域了,只有我們上面的情況(將一個域中的設置cookie的操作放到frame中)才可以跨域的。
Firfox,opera下是不支持p3p的,不過可以不用指定p3p,也可以讀取到cookie.另外在網上找的幾個解決方案
解決方法是:
view plaincopy to clipboardprint?
// www.B.com里的被調用的頁面需要寫P3P頭,從而解除IE對寫Cookie的阻止
context.Response.AddHeader( " P3P ", " CP=CAO PSA OUR ");
// www.A.com里通過ajax調用www.B.com里的內容時,是跨域訪問,需要使用jsonp,為配合其工作需要添加下面兩句,生成jsonp返回
context.Response.ContentType = " text/plain ";
context.Response.Write( string.Format( " {0}('OK') ", context.Request[ " callback "]));
view plaincopy to clipboardprint?
// jsonp調用進行跨域訪問
jQuery.ajax({
url: url,
type: ' GET ',
data: data,
dataType: ' jsonp ',
success: function (data) {
window.location.href = toURL;
}
});
最近在發現使用Taobao的時候的一個小細節,於是便萌發起了寫這篇文章。
當我們在 www.taobao.com 中進行登錄之后,然后直接切換到 www.tmall.com 域名下,發現www.tmall.com首頁的最頂部馬上顯示成了”您好, andyfaces“,於是便對此處的實現機制進行分析。
首先,用戶名應該是存儲在cookie中的,於是在taobao.com的域名中用 firefox看到用戶名確實是存儲在 cookie, 而tmall.com中沒有存儲該cookie:
可以確定的是對於cookie來說肯定是不允許垮域訪問的。無論是通過JS還是Server端程序來說都是如此,那么tmall.com是如何訪問到taobao.com下的cookie的呢?
於是打開 tmall.com,然后使用firebug來進行調試,發現了一條這樣的請求語句
其頁面的JS代碼為:
<script>
KISSY.getScript( " http://www.taobao.com/go/app/tmall/login-api.php? "+Math.random())
</script>
看到這里之后於是也大概知道他如何處理了的,為了確認一下,於是搜索一下 KISSY.getScript 函數代碼,確實采用了JS跨域的 JSONP 解決方案:
getScript: function(url, success, charset) {
var isCSS = RE_CSS.test(url),
node = doc.createElement(isCSS ? ' link ' : ' script '),
config = success, error, timeout, timer;
node.src = url;
node.async = true;
scriptOnload(node, function() {
if (timer) {
timer.cancel();
timer = undef;
}
S.isFunction(success) && success.call(node);
// remove script
if (head && node.parentNode) {
head.removeChild(node);
}
});
head.insertBefore(node, head.firstChild);
}
其原理是通過動態create js include 動態加載js,然后為該script節點bind onload事件或判斷onreadystatechange,其具體細節可以參考以上 scriptOnload 的函數的處理。 當js加載完成之后 采用回調方式來執行 success 函數。
為了進一步確實,於是使用 Jquery的 $.getScript 來測試一把,首先在 taobao.com下進行登錄成功,然后隨便在本地寫了一個測試頁,通過以下語句:
$.getScript( ' http://www.taobao.com/go/app/tmall/login-api.php?0.6783450077710154 ', function(){
console.log( " the taobao.com cookie object: " + userCookie + " username: " + userCookie._nk_);
});
Firbug結果:
其實大致原理如此,通過在www.taobao.com 的server端提供一個獲取當前域下所有cookie的 php的請求地址,然后該php獲取到cookie之后將期並成 js 代碼,也就是以上第二個截圖所看到的。然后再在 tmall 采用 jsonp 的方式跨域加載該 js 代碼,從而實現 cookie 的跨域訪問。