跨域登錄
跨域登錄跨域登錄是一個比較煩人的事情,往往我們需要寫文章記錄下來,或者探討,或者拋磚引玉的問題,都是令人頭疼的。上次簡單得寫了一篇關(guān)于跨域登錄的文章,只講了大體的實(shí)現(xiàn)過程。但是現(xiàn)在碰到了更大的問題,這
跨域登錄
跨域登錄是一個比較煩人的事情,往往我們需要寫文章記錄下來,或者探討,或者拋磚引玉的問題,都是令人頭疼的。上次簡單得寫了一篇關(guān)于跨域登錄的文章,只講了大體的實(shí)現(xiàn)過程。但是現(xiàn)在碰到了更大的問題,這篇文章將會介紹這個成敗,并探討、實(shí)現(xiàn)新方案的可行性。
跨域登錄需要一張通行證,也可以稱之為票據(jù)。就老衲現(xiàn)在知道和實(shí)驗(yàn)的方式一共有三種:
1、瀏覽器get 參數(shù);
2、session
3、cookie
每個都有特定的條件,以及需要處理的細(xì)節(jié),也會帶來一些新問題。根據(jù)經(jīng)驗(yàn),瀏覽器帶參數(shù),將會使系統(tǒng)開發(fā)得不像個東西,至少我是這么認(rèn)為的。需要考慮對這個地址參數(shù)處理的各種策略,一開始我就否定了這個方案。在我需要改造的項(xiàng)目中有4個獨(dú)立域名,跳轉(zhuǎn)來跳轉(zhuǎn)去,將會給用戶造成極為不爽的體驗(yàn)。
session 也可以解決問題,但是有一個問題無法解決。
先看看怎么用session 解決問題。假設(shè)現(xiàn)在有a.com,b.com ,現(xiàn)在開兩個子域名: passport.a.com 和passport.b.com 。然后把這兩個域名指向同一個站點(diǎn),也就是在同一個站點(diǎn)的http 投綁定這兩個域名。
那么登錄的時(shí)候,在 passport.a.com 上登錄成功,就可以設(shè)置一個session ,那么在兩個系統(tǒng)當(dāng)中都是可以通過代理文件,訪問到這個session 的,這個方案確實(shí)是可行的。但是session 只能保持20分鐘,新問題就出來了。假設(shè)這個用戶20分鐘沒有去操作,而打開了另外一個域名,那么這個判斷就失效了。訪問本域是沒問題的,cookie 還在那里擺著。 我比較傾向于用cookie 來解決問題。上一次設(shè)計(jì)的系統(tǒng),可以說極其簡單。4個系統(tǒng),有3個是asp.net 的,還有個論壇是asp 的(不用說就是動網(wǎng)的了)?,F(xiàn)在就有四套登錄系統(tǒng)。如果整體上全部改造,老衲認(rèn)為成本太大了。后來四處逛網(wǎng)站,借鑒了Sohu 的登錄方式,但是只做了個體驗(yàn)的實(shí)現(xiàn),如果全部實(shí)現(xiàn)了就不會現(xiàn)在在這里探討這個問題了。解決方案就是javascript iframe實(shí)現(xiàn)的。
本來想用純javascript 實(shí)現(xiàn),然后給src 的文件帶參數(shù),但是實(shí)際開發(fā)過程中,應(yīng)該是我的js 水平太菜,所以感覺不到想要的那種效果。后來就采用了javascript iframe的方式來實(shí)現(xiàn)。是無刷新的那種哦,呵呵。
// JavaScript Document
//
javascript 使用很簡單,點(diǎn)擊登錄也就是調(diào)用了OnSign 方法,將會向
passport.c.com/jslogin.aspx發(fā)出請求。
Response.AddHeader("P3P", @"CP=""CURaADMaDEVaPSAoPSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR""");
Response.AddHeader("Content-Encoding:", "utf-8");
string url = Request.ServerVariables["HTTP_REFERER"];
if (!string.IsNullOrEmpty(url) &&url.IndexOf('#') > 0)
{
url = url.Substring(0, url.IndexOf('#'));
,}
string username = Request["username"];
string password = Request["password"];
Response.Write("");
Response.End();
}
password = HttpUtility.UrlDecode(password, System.Text.Encoding.UTF8); password = EncryptHelper.MD5(password);
password = password.Substring(8, 16);
BBSUserbu = BBSUserHelper.Current(username, password);
if (bu.UserID != 0)
{
DateTimedt = DateTime.Now;
string save = RequestHelper.Get("save");
//dt = string.IsNullOrEmpty(save) ? dt.AddHours(2) : dt.AddMonths(1); dt = dt.AddMonths(1);
Guid g = Guid.NewGuid();
StatUserHelper.Delete(bu.UserID);
StatUsersu = new StatUser();
su.UserID = bu.UserID;
su.UserName = bu.UserName;
su.ExpireTime = dt;
su.CreateTime = DateTime.Now;
su.Guid = g;
,su.Password = bu.Password;
su.ID = StatUserHelper.Add(su);
string cachedate = su.Guid.ToString() "|" su.ID;
string cacheuser = bu.UserID "|" bu.UserName;
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, cachedate, DateTime.Now, dt, true , cacheuser);
string authTicket = FormsAuthentication.Encrypt(ticket);
HttpCookieUserCookie =
new HttpCookie(FormsAuthentication.FormsCookieName, authTicket); UserCookie.Domain = ".c.com";
UserCookie.Expires = ticket.Expiration;
if (Response.Cookies[FormsAuthentication.FormsCookieName] == null ) Response.Cookies.Add(UserCookie);
else
Response.Cookies.Set(UserCookie);
Response.Write("'" cachedate "|" HttpUtility.UrlEncode(bu.UserName, Encoding.UTF8) "';");
Response.End();
}
Response.Write("'0|';");
Response.End();
jslogin 的代碼就是個驗(yàn)證的過程,加的P3P 頭,是可以跨域?qū)懭隒ookie 的保證。這里使用的是.Net 的Forms 驗(yàn)證,要保持和其它域名加密方式以及名稱的統(tǒng)一。假如有兩個域同時(shí)指向一個站點(diǎn)的話。
這里返回javascript 并且操作iframe 的父窗口,改變地址,而引用的js 會監(jiān)視地址欄,發(fā)現(xiàn)數(shù)據(jù),根據(jù)數(shù)據(jù)的格式,判斷是否驗(yàn)證成功,如果成功了,那么會向各個站點(diǎn)下的一個SetLogin 文件發(fā)出請求,當(dāng)然被請求的頁面需有P3P 頭。
這樣在一個地方登錄,實(shí)際上是同時(shí)向其它域名寫入Cookie ,退出的原理也是一樣的。 但是,在Maxthon 中Iframe 操作父窗口地址這個操作是不允許的,它認(rèn)為這個不安全,我倒是沒覺得。這個問題還不太大,畢竟有Maxthon 的用戶不是太多,即使用了,告訴他不能用,他也會用IE 。
但是IE8 beat 2這種操作方式將會彈出新窗口。在IE8 beat2中使用Iframe 解決方案就會變得體驗(yàn)很不好。而且還給老衲帶來了心靈上的傷害,以后不敢什么都寫在客戶端了,瀏覽器版本一變,對整體影響太大了。
下一個可替代方案就是使用反向代理,sohu 的無刷新登錄就是基于這個實(shí)現(xiàn)的據(jù)說,目前還在研究中。上一次因?yàn)闀r(shí)間急迫,沒有時(shí)間仔細(xì)實(shí)驗(yàn)。這次是沒辦法躲過去了。