第三部分 實作單點登入(Single Sign On)
“等了好久終于等到今天,寫了好久終于就快完結,但是網友的反應卻讓我有一些的傷心。盼了好久終于盼到今天,忍了好久終于把此文撰寫,那些受冷落的無奈早就無所謂,累也不說累”(歌詞《今天》新演繹)。看着人家的 Blog 文章的評論是一條接一條,再瞧瞧自己:“無人問津呐,真…無…奈……唉,沒人理我,還是回家吧。”“哎,還沒開始寫,怎麼就走了?回去幹什麼呢?”回去寫作業去啊,上回交待的課外作業你做了沒?(注:http://blog.csdn.net/cityhunter172/archive/2005/11/13/528463.aspx 在第二部分第六節布置的課外作業:此項目有兩部門使用,其中每個部門分别都有些特定的頁面僅供本部門使用者浏覽使用,請問該如何使用 Web.config 達到效果?)
不知有多少人做了作業,其實答案并不難。隻需要在驗證使用者名與密碼後,取得該使用者的部門名稱或部門代碼,把它作為判斷的依據就行了。最好不要用部門的數字ID,那樣不利于以後的維護。
有一個秘密,一般人我不告訴他。Web.config 中的 <location> 節點的path 屬性可以是一張具體頁面的相對 URL 路徑,如下:<location path ="ManageSys/Auditing.aspx">
好了,接下來就要揭開“比根目錄Web.config 的作用範圍還大的配置檔案”之謎啦,它就是藏匿在 Windows 系統目錄下,支配整個 .Net Framework 配置的傳說中的Machine.config !!下面請大家以熱烈的掌聲,歡迎我們這位神秘俠客的閃亮登場……
九、 Machine.config
Machine.config ,性别不詳,年齡未知,家庭出身:XML。深藏于“雲深不知處”的作業系統目錄下的某某地方(注:C:/WINDOWS【或 WINNT 】/Microsoft.NET/Framework/v1.1.4322【或 v1.0.3705 】/CONFIG),控制着“更上一層樓”的 .NET Framework 的本機配置。接下來簡要的講解一下它的内容,以及它與 Web.config 的關系。
經過“松下問童子”,我們好不容易找到這位隐者,打開一看,乖乖,足有 3700 多行!!“叫我怎麼能不難過,偶隻想看看是啥結構,可内容實在是太多太繁瑣……”還記得偶經常對同僚說的一句話麼:“辦法是人想出來的!”它不是有三千七百多行嗎,那我們就不管三七能否得出二十一啦,把它拷出來先。它不是 XML 出身嗎,那咱們就還其正身,重新命名為“machine.xml”。接着用 IE 浏覽器将這位改頭換面的隐者打開,把節點與注釋一一合攏。這回你看到了吧,是不是很有成就感?你要是想謝謝我,就讓我看到你在此文下面的評論吧。多多益善,呵呵。
Machine.config 與 Web.config 是啥關系?四個字 —— 父子關系。記得我在第二部分第五節講解 Web.config 作用範圍的時提到兩點 —— 繼承與覆寫(詳見http://blog.csdn.net/cityhunter172/archive/2005/11/13/528463.aspx),在此也同樣适用。
1、 Machine.config 中的設定将作用于運作在本機的所有站點及其虛拟目錄,遇到子目錄将一直繼承下去。
2、 Web.config 中的設定将覆寫由 Machine.config 中繼承下來的對應的節點設定
說到這,再告訴大家一個秘密 —— “世上本無秘密,知道的人多了,便成了不是秘密的秘密!”
a、 Machine.config 中的 <system.web> 節點所有内容都能出現在項目根目錄下的 Web.config 中,也就是說能在 Web.config 中的内容已經在 Machine.config 中一一列出;
b、 其中 <system.web> 節點下的 <pages> 還能出現在頁面上,如: HTML 視圖下,在WebForm1.aspx 的第一行加上<pages> 的節點内容validateRequest="false" (此句意思是不對WebForm1.aspx頁面文本框輸入的值,是否包含 “<” “>” 等等具有危險性的代碼進行檢查,下一節将具體運用到)
<%@ Page language="c#" Codebehind="WebForm1.aspx.cs" AutoEventWireup="false" Inherits="FromTest.WebForm1" validateRequest="false" %>
十、 單點登入(Single Sign On)的前提條件
之前說了這麼多關于 Machine.config 的事,都是為了實作單點登入作鋪墊,那何為單點登入(Single Sign On)?從字面了解就是在一個地方登入,通常運用于 ASP.NET 分布式環境中(跨單個伺服器上的多個應用程式或在網絡場中)的 Forms 身份驗證。打個比方,就好比現在 Sohu(搜狐) 與 Chinren(中國校友錄) 的做法,我在 Sohu 登入以後就不需要在 Chinaren 登入了。台灣與香港又把 Single Sign On 稱之為“單一登入”。
要想實作此功能,首要條件是需要一組用于加密與驗證加密的密鑰。它們位于 Machine.config 中,修改 <system.web> 節點下的 <machineKey> 節點屬性,如下:
<machineKey firstKey="172" copyrightKey="Cityhunter172" validationKey="AD117F2F286CDCB15A9D1D4535E16DB0248026939**AUTHOR**CITYHUNTER172****WEBSITE**172*MEIBU*COM****MAILTO**
[email protected]*COM*****F2F286CDCB15A9D1D4535E16DB0248026939" secondKey="meibu"
decryptionKey="3C89AE62AD117F2F286CDCB15A9D1D4535E16DB0248026939" validation="SHA1" thirdKey="com" />
1、 validationKey 為用于驗證加密資料的密鑰。最小長度為 40 個字元(20 位元組),最大長度為 128 個字元(64 位元組)。
2、 decryptionKey 為用于加密資料的密鑰。長度隻有 16 個字元(8 位元組)與 48 個字元(24 位元組)兩種。
3、 validation 為用資料驗證使用的加密類型。擁有“SHA1”“MD5”“3DES”三種方法
4、 大夥參照上述 <machineKey> 試着在WebForm1.aspx運作下列語句:
this.TextBox2.Text ="ht"+"tp"+"://"+firstKey+"."+secondKey +"."+thirdKey
大家在修改之前請先備份一下 Machine.config ,到時要是出錯可别怪我沒提醒你。以上密鑰并不是胡亂得來的,接下來向大家介紹生成密鑰的方法。
我們把上一節中提到的 WebForm1.aspx 拖入本項目的 Public 目錄下,再往頁面上拖入一個 TextMode=MultiLine 的TextBox3 與一個 Button 編寫按鈕事件與函數:
private void Button1_Click(object sender, System.EventArgs e)
{
string decStr = this.CreateKeyString(int.Parse(this.TextBox1.Text));
string valStr = this.CreateKeyString(int.Parse(this.TextBox2.Text));
this.TextBox3.Text=string.Format("<machineKey validationKey=/"{0}/" decryptionKey=/"{1}/" validation=/"SHA1/"/>",valStr,decStr);
}
/// <summary>
/// 生成加密型強随機 Key 值
/// </summary>
/// <param name="i">Key 的有效長度:
/// decryptionKey 的有效值為 8 或 24;
/// validationKay 的有效值為 20 至 64
/// </param>
private string CreateKeyString(int i)
{
System.Security.Cryptography.RNGCryptoServiceProvider rng = new System.Security.Cryptography.RNGCryptoServiceProvider(); //加密随機數生成器
byte[] bt = new byte[i];
rng.GetBytes(bt);//用加密型強随機值序列填充位元組數組
System.Text.StringBuilder str = new System.Text.StringBuilder();
for(int j= 0;j<i;j++)
{
str.Append(string.Format("{0:X2}",bt[j])); //轉換成大寫的十六進制文本
}
return str.ToString();
}
每次點選按鈕生成密鑰都不同,大家不妨多點幾次。切換至 HTML 視圖,到WebForm1.aspx 第一行把 validateRequest="false" 去掉,然後再多點幾次 Button1試試,看看會有什麼效果,嘿嘿………
十一、 單點登入(Single Sign On)的站點示例
将上述 TextBox3 産生的文本,覆寫Machine.config 中的,現在你的機器已經具備了單點登入的條件。大夥可以再建立一個項目 FormTest2 ,從 FormTest2 登入後直接輸入 FormTest 中的Default.aspx 的網址(http://localhost/FormTest/ Default.aspx),反之亦可。
下面結合執行個體講解:偶在山東每步科技網站申請了一個免費二級域名 172.meibu.com,并下載下傳了每步的 4.0 版的動态域名解析用戶端。現在使用 ADSL 拔号上網,也就是說我的電腦已經成了 Web 伺服器,同時支援 SQL Server 、Oracle 空間高達 200 G 想怎麼弄就怎麼弄,夠牛吧,嘿嘿。布署上來的項目有環勝數位網站、權限管理系統、IT 内部管理網,以上三個項目是偶一人全權開發的。所謂全權就是從資料庫存儲過程寫到 .cs 代碼再到 javascript ,最後到美工都是偶一手搞定的。^_^ 我把這三個不相幹的項目做成了單點登入的模式,加上整合站點的首頁面,共有四個地方可以進行登入。因為使用者 Table 的結構不同,是以隻有一個入口能在進入後,在跳轉站點時不會出錯,那就是在整合頁面登入。
現在我想把環勝數位這個站點單獨脫離出來,而剩下的兩個站點繼續實作單點登入,該怎麼做呢?或者是我的 ASP.NET 的空間是租的,服務商肯定不可能讓我修改 Machine.config ,我又咋辦哩?“辦法是人想出來滴!!”,根據上述 Machine.config 與 Web.config 的關系,我們可以把 <machineKey> 節點放入項目根目錄下Web.config的 <system.web> 節點。如下:
1、 權限管理系統項目的 Web.config 用于 Form 認證的設定
<machineKey validationKey="AD117F2F286CDCB15A9D1D4535E16DB0248026939**AUTHOR**CITYHUNTER172****WEBSITE**172*MEIBU*COM****
MAILTO**[email protected]*COM*****F2F286CDCB15A9D1D4535E16DB0248026939" decryptionKey="3C89AE62AD117F2F286CDCB15A9D1D4535E16DB0248026939" validation="SHA1" />
<authentication mode="Forms">
<forms loginUrl="Login.aspx" name="172.MEIBU.COM_WARRANT"></forms>
</authentication>
<authorization><deny users="?"></deny></authorization>
2、 IT 内部管理網項目的 Web.config 用于 Form 認證的設定
<machineKey validationKey="AD117F2F286CDCB15A9D1D4535E16DB0248026939**AUTHOR**CITYHUNTER172****WEBSITE**172*MEIBU*COM****
MAILTO**[email protected]*COM*****F2F286CDCB15A9D1D4535E16DB0248026939" decryptionKey="3C89AE62AD117F2F286CDCB15A9D1D4535E16DB0248026939" validation="SHA1" />
<authentication mode="Forms">
<forms loginUrl="Login.aspx" name="172.MEIBU.COM_IT"></forms>
</authentication>
<authorization><deny users="?"></deny></authorization>
大家可能會迫不急待的去試一把,偶贊成這樣的做法,因為事實是檢驗真理的唯一辦法。你不去試着自己動手,光看我在這說是很難提高的。先别急,我已經知道你想說什麼,聽聽我慢慢向你解釋:
a) 兩個項目Web.cinfig的<machineKey> 節點確定以下幾個字段完全一樣:validationKey 、decryptionKey 、validation
b) 兩個項目的 Cookie 名稱必須相同,也就是 <forms> 中的 name 屬性,這裡我們把它統一為 name ="172.MEIBU.COM_PROJECT"
c) 注意區分大小寫
在整合的過程中,我把遇到的問題向大夥說一下,以免你們走同樣的路。
1) 首先應該是使用者管理的問題,把兩個項目的使用者整合在一起,可不是一件容易的事,原則是建立一個新的 Table 隻存放帳号與密碼,用賬号做關聯,編寫觸發器,做到 Table 之間的同步;
2) 不要指望兩個項目間用 Session 進行傳值,兩個應用程式的 Session 是無法共享的。網上有人曾把類庫(編譯後的 .dll 文檔)放入同一個 bin 檔案夾實作過 Session 共享,這樣的做法實際上是把兩個項目變相合并成一個應用程式,不是我們所想要的,理由很簡單:Sohu 與 Chinaren 的伺服器分處兩地該怎麼辦?
3) 項目間的傳值,可用 Cookie 實作。在第一部分的第三節(http://blog.csdn.net/cityhunter172/archive/2005/11/06/524043.aspx)我們介紹了隻要運作 System.Web.Security.FormsAuthentication.SetAuthCookie 方法即可實作登入,單點登入的實質就是含有身份驗證票的 Cookie 能在項目間共用。
接下來,有必要向大家介紹一下 Cookie 在 .Net 中的用法。
十二、 Cookie 在 ASP.NET 中的用法
大家也許和我一樣,很少在 ASP.NET 中使用 Cookie ,傳參數呀,存變量呀,用的比較多的是 Session 或 ViewState 以及隐藏控件,有的幹脆用“ ? ”的請求方式。
1、 Cookie 存放的目錄
Cookie 是存放在用戶端的東東,放在“Temporary Internet Files”目錄,是以說存在安全性的問題。大夥可通過以下方式找到具體位置:打開控制台 → Internet 選項 → 正常 → Internet 臨時檔案 → 設定 → 即可看到“目前位置”,→ 點選“檢視檔案”将直接打開該檔案夾,你也可以點選“移動檔案夾”變更它所在的位置。參照下圖
2、 Cookie 的有效期
從上圖我們可以清楚的看到每個 Cookie 文檔的“截止期”(即為有效期)。在有效期内,當登入計算機的使用者 Administrator 再次通路 172.meibu.com 時,那麼 IE 就會在請求頁面的同時,連同上述的名稱為“Cookie:[email protected]”的Cookie 文檔内容一起發送給伺服器。
若該文檔包含多個 Cookie 的值時,截止期則以最後的失效期為準。
3、 Cookie 的類型
這裡我們按有效期來分,分為兩種:
a)即時型
指的是關閉浏覽器(所有浏覽 172.meibu.com 的 IE)後,Cookie 便失效,此類 Cookie 不會在“Temporary Internet Files”目錄出現。其實它也有截止期的,為“0001-01-01”
b)持久型
就是已指定具體“截止期”的,能夠在“Temporary Internet Files”目錄裡面找到的 Cookie
4、 Cookie 的内容
輕按兩下打開“Cookie:[email protected]”,我們看到以下内容,如下圖:
上圖中,“■”是換行符,你若是要打破什麼鍋來問我到底是怎麼知道的話。我倒是會很樂意的告訴你:這就是經驗!偶從學習 C# 那刻起,就拿第一個 Windows 程式 記事本 來開刀,儲存文檔時得來的經驗。
是以伺服器讀出來的格式如下圖:
發送上述 Cookie 的 .cs 代碼為:
System.Web.HttpCookie ck = new HttpCookie("ckValue0");
ck["Author"] ="CityHunter";
ck.Expires = System.DateTime.Now.AddMinutes(10);//若不指定,則為即時型 Cookie
//ck.Path="/FormTest/ManageSys"; //設定 Cookie 的虛拟路徑,注意一定要以“/”開頭,否則為無效 Cookie ;請大家自行看一下它與在客房端的 Cookie 文檔“名稱”與 “Internet 位址”的關系
Response.Cookies.Add(ck);
ck = new HttpCookie("ckValue1"); //重新建立一個名為 ckValue1 的 Cookie
ck.Expires = System.DateTime.Now.AddMinutes(20); //即刻起 20 分鐘後失效
ck["E_Mail"] ="[email protected]"; //設定 ckValue1 中的 E_Mail 值
ck["PersonalWeb"] ="172.meibu.com";
Response.Cookies.Add(ck); //添加此 Cookie
6、 取回已發放 Cookie 的值
Response.Write(Request.Cookies["ckValue0"]["Author"]+"<br>");//用不着說明了吧
Response.Write(Request.Cookies["ckValue1"]["E_Mail"]+"<br>");
Response.Write(Request.Cookies["ckValue1"]["PersonalWeb"]);
好久沒有出作業啦(何出此言?),這第三篇呀,可是花了偶兩個星期的業餘時間調試、總結、撰寫哪,都說時光貴如金,不知我花的這些時間能換來多少銀子?換銀子,我看是沒指望啦,能得到閣下的一句評論,偶也滿足了。記住,你的評論就是偶繼續寫下去的動力。
作業:給 Cookie 賦于以下值,怎樣得到它的正确值
ck["str1"] ="2222";
ck["str"] ="str0=11111&str1=223";
可以肯定的是Request.Cookies["ckValue1"]["str"] 得不到 “str0=11111&str1=223”這個字串,大家不妨試一下 Request.Cookies["ckValue1"]["str1"] 會得到意想不到的字串喲。
提示:使用 Server.UrlEncode()與Server.UrlDecode()
十三、 發放永久性的驗證 Cookie
終于……終于……最後一個章節,蓦然回首,洋洋灑灑十二章。沒想到年少時寫不完作文的偶,居然也能編出幾千餘字的文章來呀,不得不佩服偶自己呀!再回首,一大片暈倒的人……。永遠到底有多遠?永久究竟是多久?隻有天知道。
大家登入 CSDN的時候是否留意到一個“2 周内不用再登入”的複選框,它又是怎麼做到的呢?大家是否曾遇到過這樣的困惑:在執行System.Web.Security.FormsAuthentication.SetAuthCookie 時明明已指定createPersistentCookie 為 true 為何關閉浏覽器仍不能直接通路網站?下面我們就這個問題給大家解釋一下,且介紹如何手工建立身份驗證票并加入 Cookie 中。
System.Web.Security.FormsAuthenticationTicket tk = new System.Web.Security.FormsAuthenticationTicket(
1, //指定版本号:可随意指定
"Admin", //登入使用者名:對應 Web.config 中 <allow users="Admin" … /> 的 users 屬性
System.DateTime.Now, //釋出時間
System.DateTime.Now.AddYears(100), //失效時間:100 年以後,夠永夠久了吧
false, //是否為持久 Cookie:尚未發現有何用,至少目前偶還不知,下面會有說明
"測試使用者資料"//使用者資料:可用 ((System.Web.Security.FormsIdentity)User.Identity).Ticket.UserData 擷取
);
string str = System.Web.Security.FormsAuthentication.Encrypt(tk);//加密身份驗票
//聲明一個 Cookie,名稱為 Web.config 中 <forms name=".APSX" … /> 的 name 屬性,對應的值為身份驗票加密後的字串
System.Web.HttpCookie ck = new HttpCookie(System.Web.Security.FormsAuthentication.FormsCookieName,str);
//指定 Cookie 為 Web.config 中 <forms path="/" … /> path 屬性,不指定則預設為“/”
ck.Path=System.Web.Security.FormsAuthentication.FormsCookiePath;
//此句非常重要,少了的話,就算此 Cookie 在身份驗票中指定為持久性 Cookie ,也隻是即時型的 Cookie 關閉浏覽器後就失效;是以上面我說:我是真的還不知在身份驗票中指定為持久性 Cookie 有何用。
ck.Expires = System.DateTime.Now.AddYears(100);
Response.Cookies.Add(ck); //添加至客房端
後記
此系列文章共三部分,曆時一個月完成(2005-11-05 ~ 2005-12-06)。以上是我學習并用于實踐的一些經驗,在此拿出來與大家一起分享。代碼都是經過調試的,如有任何疑問,可在 CSDN 論壇(http://community.csdn.net/)找到我,我的 ID 是 cityhunter172 (可用此 ID 發短消息給我),昵稱為 寒羽楓,歡迎大家批評指正。
第四部分 Form 認證的補充
前三篇在 CSDN 論壇公布後,效果如同“神仙放屁——果然不同凡(反)響”。為感謝廣大網友的熱情與支援,這不,經過這一陣子的醞釀、修煉,特意準備了這第四響。
之前我們講述的使用 Form 認證實作單點登入,正如網友所說的那樣,隻能在同一域名下使用。對于跨域名的單點登入,除了使用 Passport 認證外,我們還是可以用 Form 認證的,隻是要講究方法而已啦。正所謂“山不轉水轉,人不轉心轉”。
一、 跨域名的解決思路
在MSDN 2003 上搜尋關鍵字“Passport”,偶找到一篇“Passport 身份驗證提供程式”。文章講述了 Passport 的認證原理,共 8 條,我就不多說了,大夥自個看吧。其中有一句話,引起偶的注意:“……響應在查詢字元串中包含一個加密的 Passport Cookie……”。也正是此句才有了下面的思路。
所謂認證的通過與否,其實質就是檢測有無發放有效的 Cookie ,使用 Form 也好,運用 Passport 也罷,都是 Cookie 在起作用。也就是說,我們隻要把有效的 Cookie 在登入後一次性發放給用戶端就得了。
二、 跨域名、跨伺服器的單點登入方法
1、 如何在本機模拟跨域名、跨伺服器的Single Sign On
隻要浏覽網址不同就相當于不同域名,在本機至少有以下三種。它們雖然是同一項目,彼此卻不能共用 Session與 Cookie ,也就無法共享身份驗證票:
a). http://localhost/FormTest/Login.aspx
b). http://127.0.0.1/FormTest/Login.aspx
c). http://My_Computer_Name/FormTest/Login.aspx //以電腦名稱浏覽站點
d). http://192.168.0.8/FormTest/Login.aspx //以網卡位址浏覽站點
e). http://172.meibu.com/FormTest/Login.aspx //擁有國際域名
2、 在 ASP.NET 中如何送出給其它頁面
用過ViewState 的大概都知道,ViewState是儲存在用戶端的。不知大夥注意沒有,ASP.NET 為每張 .aspx 頁面都配備了獨自的 ViewState,且被解析後都是以一個 name=" __VIEWSTATE" 的隐藏控件值來儲存ViewState。每次頁面送出,伺服器都會檢查該控件的值有無被篡改,如此一來就注定 .aspx 隻能送出給本頁。伺服器是死的,人是活的,我們不能被這些條條框框限定死了,我們要把程式寫成活的。
下面咱們從 http://localhost/FormTest/Login.aspx 輸入使用者名與密碼,然後送出給http://127.0.0.1/FormTest/Public/LoginTransfer.aspx 。Login.aspx與LoginTransfer.aspx都包含使用者名輸入框一個、密碼輸入框一個、登入按鈕一個。在 Login.aspx 頁面加入以下代碼:
this.Btn_Login.Attributes["onclick"]="SingleSignOn()"; //指定執行腳本事件
在 Login.aspx 頁面上插入以下腳本:
<script language="javascript">
function SingleSignOn()
{
//隻能用腳本改變指定 Form 送出的對象
document.getElementById("Form1").action="http://127.0.0.1/FormTest/Public/LoginTransfer.aspx?FromUrl="+window.location.href;
//把隐藏控件 __VIEWSTATE 中的值變更為 LoginTransfer.aspx 解析後出現的值,以實際看到的值為準
document.all.__VIEWSTATE.value = "dDwtMTkyODUzMTMyNzs7Pv1cp2RaxUcr5hGYf8ILX9/EMKy8";
}
</script>
注意事項
a). LoginTransfer.aspx 出現的控件及其 ID ,必須能夠在 Login.aspx 找到
b). 控件的 ID 必須一緻,且能一一對應
c). 關于 __VIEWSTATE中的值,它與頁面控件ID 無關,與浏覽該頁面的網址無關,目前我隻知道和控件的數量、類型、名字空間(namespace FormTest.Public )以及存在的 ViewState有關系。大家在測試時,以直接浏覽http://127.0.0.1/FormTest/Public/LoginTransfer.aspx 後,檢視頁面源檔案所看到的值為準。
d). 送出後,将觸發并執行LoginTransfer.aspx 中的Btn_Login_Click 事件
3、 基本思路
各個站點的登入頁面統一将使用者名與密碼送出給 LoginTransfer.aspx ,同時各個站點需要一個增加 Cookie 的頁面,用于将加密後的身份驗證 Cookie 添加至用戶端。此乃經過一番考量後,最終确定的可行性方案。
4、 第一種思路——天女散花
何謂天女散花,就是把 Cookie 在登入後一次性全發放出去,就如同天仙在空中散花一樣,場面是何等的壯觀。下面開始寫代碼:
為更好的區分,我們将負責添加 Cookie 的頁面分開命名:
a). http://localhost/FormTest/Public/AddCookie_A.aspx
b). http://127.0.0.1/FormTest/Public/AddCookie_B.aspx
c). http://My_Computer_Name/FormTest/Public/AddCookie_C.aspx
這三張頁面的功能一樣,是以代碼也就相同啰
private void Page_Load(object sender, System.EventArgs e)
{
string from = Request["FromUrl"]; //起始 URL 路徑
string next = Request["NextUrl"]; //還需要跳轉的 URL
string key = Request["CookieTicket"]; //已加密的 Cookie 文本
if(key != null && key !="")
{
System.Web.HttpCookie ck = new HttpCookie(System.Web.Security.FormsAuthentication.FormsCookieName,key);
ck.Path=System.Web.Security.FormsAuthentication.FormsCookiePath;
ck.Expires = System.DateTime.Now.AddYears(100);
Response.Cookies.Add(ck); //将傳過來的已加密的身份驗證票添加至客房端
string url = next.Split(';')[0]; //從 URL 中拆分出将要跳轉的下一張頁面
next = next.Replace(url+";",""); //帶入下一輪跳轉的字串
if(url!="")
{
//跳至下一頁面 Response.Redirect(url+"?CookieTicket="+key+"&FromUrl="+from+"&NextUrl="+next);
}
else //已沒有下一頁面可供跳轉
{
Response.Redirect(from); //回到起始頁面
}
}
}
接下來編寫 LoginTransfer.aspx 的代碼:
//頁面常量 allLoginUrl 存放所有站點的 AddCookie.aspx 的 URL,注意以 ; 分隔
public const string allLoginUrl =
"http://localhost/FormTest/Public/AddCookie_A.aspx;"
+"http://127.0.0.1/FormTest/Public/AddCookie_B.aspx;"
+"http://My_Computer_Name/FormTest/Public/AddCookie_C.aspx;";
偶已在上面講述了,如何點選 Login.aspx 中的登入按鈕Btn_Login将使用者名與密碼送出給 LoginTransfer.aspx ,并執行LoginTransfer.aspx 中的Btn_Login_Click 事件。
private void Btn_Login_Click(object sender, System.EventArgs e)
{
string from = Request["FromUrl"]; //起始 URL 路徑
string next = this.allLoginUrl;
//由于控件 ID 相同,是以此處得到的是由 Login.aspx 送出過來的使用者名與密碼
if(this.Txt_LoginName.Text=="Admin"&&this.Txt_Password.Text=="123456")
{
System.Web.Security.FormsAuthenticationTicket tk = new System.Web.Security.FormsAuthenticationTicket(1,"Admin", System.DateTime.Now, System.DateTime.Now.AddYears(100),false,"測試使用者資料" );
string key = System.Web.Security.FormsAuthentication.Encrypt(tk); //得到加密後的身份驗證票字串
string url = next.Split(';')[0]; //從 URL 中拆分出将要跳轉的下一張頁面
next = next.Replace(url+";",""); //帶入下一輪跳轉的字串
Response.Redirect(url+"?CookieTicket="+key+"&FromUrl="+from+"&NextUrl="+next); //跳至下一頁面
}
}
5、 第二種思路——後羿射日
後羿射日,意思指的是使用者點哪就跳哪。他若是點“火坑”,你也得往裡跳,因為使用者是上帝嘛。我們增加一個通行證頁面 MyPassport.aspx ,由 http://127.0.0.1/FormTest/Public/LoginTransfer.aspx 發放驗證 Cookie 後直接跳轉至 http://127.0.0.1/FormTest/MyPassport.aspx 。不要告訴我你不會,你要是真不會,那偶也沒法子啦,還得請你回頭看看,偶在第三篇是如何講述發放永久性驗證 Cookie 吧(http://blog.csdn.net/cityhunter172/archive/2005/12/06/545301.aspx)。還需要一張用作跳闆的跳轉頁面 MyTransfer.aspx 。
MyPassport.aspx 的代碼:
<a target ="_blank"
href="MyTransfer.aspx?goto=http://localhost/FormTest/Public/AddCookie_D.aspx" target="_blank" rel="external nofollow" >
美麗的天使</a>
<a target ="_blank"
href="MyTransfer.aspx?goto=http://127.0.0.1/FormTest/Public/AddCookie_E.aspx" target="_blank" rel="external nofollow" >
快樂的天堂</a>
<a target ="_blank"
href="MyTransfer.aspx?goto=http://My_Computer_Name/FormTest/Public/AddCookie_F.aspx" target="_blank" rel="external nofollow" >
大大的火坑</a>
MyTransfer.aspx 的代碼:
private void Page_Load(object sender, System.EventArgs e)
{
//擷取身份驗證票
System.Web.Security.FormsAuthenticationTicket tk =((System.Web.Security.FormsIdentity)User.Identity).Ticket;
string key = System.Web.Security.FormsAuthentication.Encrypt(tk); //每次加密後的字串都是不同的
string next = Request["goto"]; //将要跳轉的 URL
Response.Redirect(url+"?CookieTicket="+key); //跳轉至下一頁面
}
AddCookie_D.aspx、AddCookie_E.aspx、AddCookie_F.aspx 這三張頁面的代碼:
string key = Request["CookieTicket"]; //已加密的 Cookie 文本
if(key != null && key !="")
{
System.Web.HttpCookie ck = new HttpCookie(System.Web.Security.FormsAuthentication.FormsCookieName,key);
ck.Path=System.Web.Security.FormsAuthentication.FormsCookiePath;
ck.Expires = System.DateTime.Now.AddYears(100);
Response.Cookies.Add(ck); //将傳過來的已加密的身份驗證票添加至客房端
Response.Redirect("../Index.aspx"); //跳轉至你真正想帶客戶去的地方
}
6、 點評
兩者共同點:
a). 每個站點都需要一個登入的送出點、一張添加 Cookie 的頁面。
b). 因為隻能靠發放驗證 Cookie 來識别身份,是以一台電腦不能同時登入兩個帳号。
c). 都存在不同程度的安全隐患。
兩者不同點:(天女散花以下簡稱“開女”,後羿射日就簡稱“後羿”)
a). 天女一次性發放 Cookie ,如果站點較多,處理起來還是需要一些時間的。而後羿則相反,站點再多也不怕。
b). 天女在散花的過程中,如果中途被卡住,則需要一個錯誤處理機制做回退處理。後羿則不需要。
c). 天女在登入後可以直接在 IE 位址浏覽其想看的站點;而後羿則必須從通行證的跳闆頁面進入才行。
根據上述問題,給幾點建議:
a). 不要使用永久性 Cookie ,應指明身份驗證票的過期時間,注意不是 Cookie 的有效期。
b). 在身份驗證票的 UserData 中加入其它的驗證資訊或存放使用者 ID
c). 在網絡通暢的情況下,比如區域網路,站點又相對較少,建議選用天女。50 個站點之間做跳轉應該不會超過 10 秒(前提是已編譯好了,且不是初次通路)。
三、 跨域名、跨伺服器的退出方法
隻要了解了“天女散花”,退出就比較容易啦。為每個站點準備一個用于退出的頁面,如下:
a). http://localhost/FormTest/Public/Logout.aspx
b). http://127.0.0.1/FormTest/Public/Logout.aspx
c). http://My_Computer_Name/Public/FormTest/Logout.aspx
private void Page_Load(object sender, System.EventArgs e)
{
System.Web.Security.FormsAuthentication.SignOut();//删除 Cookie 中的身份驗證票
string from = Request["FromUrl"];
string next = Request["NextUrl"];
string url = next.Split(';')[0];
next = next.Replace(url+";","");
if(url!="")
{
Response.Redirect(url+"?FromUrl="+from+"&NextUrl="+next);
}
else
{
Response.Redirect(from);
}
}
對啦,還有一張 LogoutTransfer.aspx. ,代碼偶就不寫,大家自個完成吧。
5、 在 ASP.NET 頁面發放 Cookie