1. 程式人生 > >ASP.NET 安全認證(四)——巧妙實現 Form 表單認證跨站點、跨伺服器的單點登入(Single Sign On)

ASP.NET 安全認證(四)——巧妙實現 Form 表單認證跨站點、跨伺服器的單點登入(Single Sign On)

【原創】ASP.NET 安全認證(四)

——巧妙實現 Form 表單認證跨站點、跨伺服器的單點登入(Single Sign On

作者:寒羽楓(cityhunter172)

第四部分 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.aspxLoginTransfer.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 = newHttpCookie(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 = newSystem.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 吧()。還需要一張用作跳板的跳轉頁面MyTransfer.aspx 

MyPassport.aspx 的程式碼:

<target="_blank"

href="MyTransfer.aspx?goto=http://localhost/FormTest/Public/AddCookie_D.aspx">

美麗的天使</a>

<target="_blank" 

href="MyTransfer.aspx?goto=http://127.0.0.1/FormTest/Public/AddCookie_E.aspx">

快樂的天堂</a>

<atarget="_blank"

href="MyTransfer.aspx?goto=http://My_Computer_Name/FormTest/Public/AddCookie_F.aspx">

大大的火坑</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.aspxAddCookie_E.aspxAddCookie_F.aspx 這三張頁面的程式碼:

              string key = Request["CookieTicket"]; //已加密的 Cookie 文字

              if(key != null && key !="")

              {

                   System.Web.HttpCookie ck = newHttpCookie(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. ,程式碼偶就不寫,大家自個完成吧。

寒羽楓(cityhunter172

2005-12-31 18:22 完稿

轉自:http://blog.csdn.net/cityhunter172/article/details/567479