實現單點登錄(Single Sign On)

來源:http://www.rzhh.com.cn/Net/SSO.htm

代碼寫 N 久了,總想寫得別的。這不,上頭說在整合兩個項目,做成單一登錄(Single Sign On),也有人稱之爲“單點登錄”。查閱相關文檔後,終於實現了,現在把它拿出來與大家一起分享。或許大家會問:“這與標題不符呀?”別急,在下筆之前,我腦子裏想到了我剛使用 Form 認證時遇到的一些問題,以及使用過程用到的一些技巧(實乃投機取巧是也 ^_^ )。偶打初中那時,語文水平就不怎麼滴,考試常常作文寫不出來,所以寫作水平有限,還請大家海量。對了,本人不僅寫作水平有限,編程能力也不是很好,此文供大家學習交流之用,歡迎廣大勞苦羣衆拎着雞蛋、捧着鮮花前來評論。轉載請註明原創作者乃寒羽楓是也,不甚感激!

廢話也說的差不多了,言歸正傳, ASP.NET 的安全認證,共有“Windows”“Form”“Passport”“None”四種驗證模式。“Windows”與“None”沒有起到保護的作用,不推薦使用;“Passport”我又沒用過,唉……所以我只好講講“Form”認證了。我打算分三部分:

第一部分 —— 怎樣實現From 認證;

第二部分 —— Form 認證的實戰運用;

第三部分 —— 實現單點登錄(Single Sign On)

第一部分 —— 怎樣實現From 認證;

一、 新建一個測試項目

爲了更好說明,有必要新建一個測試項目(暫且爲“FormTest”吧),包含三張頁面足矣(Default.aspx、Login.aspx、UserInfo.aspx)。啥?有人不會新建項目,不會新增頁面?你問我咋辦?我看這麼辦好了:拖出去,打回原藉,從幼兒園學起……

二、 修改 Web.config

1、 雙擊項目中的Web.config(不會的、找不到的打 PP)

2、 找到下列文字 <authentication mode="Windows" /> 把它改成:

<authentication mode="Forms">

<forms loginUrl="Login.aspx" name=".ASPXAUTH"></forms>

</authentication>

3、 找到<authorization> <allow users="*" /></authorization>換成

<authorization><deny users="?"></deny></authorization>

這裏沒什麼好說的,只要拷貝過去就行。雖說如此,但還是有人會弄錯,如下:

<authentication mode="Forms">

 <forms loginUrl="Login.aspx" name=".APSX"></forms>

<deny users="?"></deny>

 </authentication>

若要問是誰把 <deny users="?"></deny> 放入 <authentication> 中的,我會很榮幸地告訴你,那是 N 年前的我:<authentication> 與 <authorization> 都是以 auth 字母開頭又都是以 ation 結尾,何其相似;英文單詞背不下來的我以爲他們是一夥的……

三、 編寫 .cs 代碼——登錄與退出

1、 登錄代碼:

a、 書本上介紹的

    private void Btn_Login_Click(object sender, System.EventArgs e)

       {

          if(this.Txt_UserName.Text=="Admin" && this.Txt_Password.Text=="123456")

           {

  System.Web.Security.FormsAuthentication.RedirectFromLoginPage(this.Txt_UserName.Text,false);

  }

       }

b、 偶找了 N 久才找到的

private void Btn_Login_Click(object sender, System.EventArgs e)

         {

              if(this.Txt_UserName.Text=="Admin" && this.Txt_Password.Text=="123456")

              {

System.Web.Security.FormsAuthentication.SetAuthCookie(this.Txt_UserName.Text,false);

     Response.Redirect("Default.aspx");

     }

}

以上兩種都可發放驗證後的 Cookie ,即通過驗證,區別:

方法 a) 指驗證後返回請求頁面,俗稱“從哪來就打哪去”。比如:用戶沒登錄前直接在 IE 地址欄輸入 http://localhost/FormTest/UserInfo.aspx ,那麼該用戶將看到的是 Login.aspx?ReturnUrl=UserInfo.aspx ,輸入用戶名與密碼登錄成功後,系統將根據“ReturnUrl”的值,返回相應的頁面

方法 b) 則是分兩步走:通過驗證後就直接發放 Cookie ,跳轉頁面將由程序員自行指定,此方法多用於 Default.aspx 使用框架結構的系統。

2、 退出代碼:

private void Btn_LogOut_Click(object sender, System.EventArgs e)

{

System.Web.Security.FormsAuthentication.SignOut();

}

四、 如何判斷驗證與否及獲取驗證後的用戶信息

有的時候,在同一張頁面需要判斷用戶是否已經登錄,然後再呈現不同的佈局。有人喜歡用 Session 來判斷,我不反對此類做法,在此我只是想告訴大家還有一種方法,且看下面代碼:

if(User.Identity.IsAuthenticated)

         {

              //你已通過驗證,知道該怎麼做了吧?

}

第二部分 Form 認證的實戰運用

話說上回,簡單地說了一下 Form 表單認證的用法。或許大家覺得太簡單,對那些大內高手來說應該是“灑灑水啦”“小 Kiss 啦(小意思)”。今天咱們來點的花樣吧:古有六扇門,拒收葉孤城;東門不颳風,吹雪姓西門;緞帶作憑證,決戰紫禁城。

五、 Web.config 的作用範圍

新建項目時, VS.Net 會在項目根目錄建立一個內容固定的 Web.config。除了在項目根目錄,你還可以在任一目錄下建立 Web.config ,條件就是應用程序級別的節點只能在根目錄的 Web.config 中出現。至於哪些是應用程序級別節點呢,這個問題嘛,其實我也不太清楚,呵呵。電腦不是我發明的,微軟不是我創建的,C# 更不是我說了算的,神仙也有不知道的,所以我不曉得是正常的。話雖如此,只要它不報錯,那就是對的。

關於 Web.config 設置的作用範圍,記住以下兩點:

1、 Web.config 的設置將作用於所在目錄的所有文件及其子目錄下的所有東東(繼承:子隨父姓)

2、 子目錄下的 Web.config 設置將覆蓋由父目錄繼承下來的設置(覆蓋:縣官不如現管)

給大家提個問題:有沒有比根目錄Web.config 的作用範圍還大的配置文件呢?看完第三部分便知分曉。

六、 學會拒絕與巧用允許

回到我們在第一回合新建的測試項目“FormTest” , 既然要進行驗證,按國際慣例,就得有用戶名與密碼。那,這些用戶是管理員自己在數據庫建好呢,還是用戶註冊、管理員審覈好呢。只要不是一般的笨蛋,都知道 選擇後者。你們還別說,我公司還真有個別項目是管理員連到數據庫去建帳號的,屬於比較特殊的笨蛋,咱們不學他也罷,還是老老實實添加兩個頁面吧——註冊頁 面(Register.aspx)與審覈頁面(Auditing.aspx)。

問題終於就要浮出水面啦,當你做好 Register.aspx 時,想訪問它的時候突然覺得不對勁,怎麼又回到了登錄頁面?你仔細瞧瞧網址,是不是成了:Login.aspx?ReturnUrl=Register.aspx 。怎麼辦,用戶就是因爲沒有帳號纔去訪問註冊頁面的呀?(這句純屬廢話,有帳號誰還跑去註冊。)我時常對我的同事說:“辦法是人想出來滴!!”

1、 新建一個目錄 Public ,用於存放一些公用的文件,如萬年曆、腳本呀……

2、 在“解決方案資源管理器”中右擊點擊目錄 Public ,新增一個 Web.config

3、 把上述 Web.config 的內容統統刪除,僅留以下即可:

<?xml version="1.0" encoding="utf-8"?>

<configuration>

  <system.web>

   <authorization><allow users="*"/></authorization>

 </system.web>

</configuration>

終於切入正題了,不容易呀。根據“覆蓋”原則,我們知道上述 Web.config 將替代根目錄 Web.config 中的 <authorization> 節點設置,即:

<allow users="*"/> 替換 <deny users="?"></deny>

註解:“allow”允許的意思;“*”表示所有用戶;

“deny” 拒絕的意思;“?”表示匿名用戶;

因此,處於 Public 目錄下的文件,允許所有人瀏覽,包括未驗證的用戶。把 Register.aspx 拖進來吧,再也不會有人阻止你瀏覽啦。

除了註冊頁面,我們還提到一個審覈頁面(Auditing.aspx),審覈權限一般都在管理員或主管手裏,並不想讓其他人瀏覽此頁面(真理往往掌握在少數人的手裏,這也是沒法子的事),怎麼辦?“辦法是人想出來滴”呵呵……新建一個管理員的目錄 ManageSys ,在此目錄下再新增一個 Web.config。內容如下:

<?xml version="1.0" encoding="utf-8"?>

<configuration>

<system.web>

<authorization>

<allow users="Admin"/>

<deny users="*"/>

</authorization>

  </system.web>

</configuration>

現在的問題就是怎麼才能知道誰是“Admin”呢,這個問題就有點象“我的鞋底有個洞”—— 天不知地知,你不知我知。閒話少說(要是有稿費多好,我就有多寫幾個字的動力,唉……),大家還記得我在第一部分的結尾嗎?什麼,忘啦!罰你回去看一百遍,記住了再回來。站住,回來!一想到你的記性,我就不放心,第一部分的瀏覽網址是http://blog.csdn.net/cityhunter172/archive/2005/11/06/524043.aspx ,回到此處的網址是http://blog.csdn.net/cityhunter172/archive/2005/11/13/528463.aspx

好了,不管那些記不好的傢伙了,大夥繼續往下看。

System.Web.Security.FormsAuthentication.SetAuthCookie(this.Txt_UserName.Text,false); //通過驗證,發放 Cookie

之前我曾強調,要注意,第一個參數很重要,重要到什麼程度?說到這,恐怕地球人都知道了——它就是allow與deny的依據。假如此處用戶填寫的是“Admin”即 this.Txt_UserName.Text = "Admin"; 那麼進入系統後,他就能訪問 ManageSys 目錄下的網頁了,其它閒雜人等一律拒之門外。

爲鞏固上述內容,給大夥留個課外作業:此項目有兩部門使用,其中每個部門分別都有些特定的頁面僅供本部門用戶瀏覽使用,請問該如何使用 Web.config 達到效果?同樣,答案在第三部分揭曉

七、 分散與集中

乍看之下,就象是馬克思列寧主義、毛澤東思想、鄧小平理論中的辯證關係,大夥放心,偶是學理科的,只明白“高舉程序員的偉大旗幟,以編寫代碼爲中心”。停……

到目前爲此,我們的測試項目“FormTest”已經擁有兩個目錄三個 Web.config ,伴隨用戶需求的多樣化,Web.config 也會越來越多,比如常用的文件上傳功能等等。衆多的 Web.config 分佈在不同的目錄裏面,維護起來肯定比較煩人。能不能集中起來管理呢,應該咋辦哩?“辦法是……”喲,有人先說出來啦。不錯,“辦法的確是人想出來滴” ,我不說,你是不是只有在一邊涼伴?開玩笑的,爲了讓更多的人記住這句話,我打算告訴你集中管理的辦法。

要想集中管理,不得不用到 <location> 節點與 path 屬性。在本項目中,我們將目錄 Public 與 ManageSys 下的設置放在根目錄下的 Web.config 裏面,如下:

<?xml version="1.0" encoding="utf-8"?>

<configuration>

<location path ="Public">

            <system.web>

                <authorization>

<allow users="*"/>

</authorization>

            </system.web>

       </location>

<location path ="ManageSys">

            <system.web>

       <authorization>

<allow users="Admin"/>

<deny users="*"/>

</authorization>

            </system.web>

       </location>

       <system.web>

 

                   <!-- 這裏放置原來根目錄 Web.config 的內容,就不列出來了 -->

 

       </system.web>

</configuration>

需要提醒的是

1、 <location> 節點的位置是在 <configuration> 的一個子節點,它與原有的 <system.web> 屬於並列關係

2、 <location> 節點只需要放入對應子目錄 Web.config 中的 <system.web> 的節點內容

八、 額外的保護

第二部分就要結束了,現在時間已是凌晨 4 點50分,我容易嘛我。認證的目的就是爲了防止他人非法瀏覽頁面,或未經許可使用某些功能。當然,世上沒有絕對的安全,如今 MD5 加密都被我們國人給破解了,就是最好的例證。

細心的人可能早就發現 ASP.NET 的安全認證只針對 .aspx、.ascx …… 等 ASP.NET 文件起作用,而對普通頁面與文件卻“視而不見”,如 .htm、.js 、.jpg 等。通過以下步驟你就可以保護你想保護的文件類型。

1、  打開 Internet 信息服務(IIS)管理器 → 右擊本項目虛擬 → 屬性,如下圖

2、  點擊按鈕“配置”,出現如下對話框:

3、  雙擊 .aspx 的應用程序擴展 → 查看對話框內容,如下圖:

4、  複製“可執行文件”的全路徑名稱後 → 點擊“取消”返回上一層對話框 → 點擊按鈕“添加”

5、  粘貼剛纔複製的內容(我的系統裝在 D 盤,所以內容爲 D:/WINDOWS/Microsoft.NET/Framework/v1.1.4322/aspnet_isapi.dll ) → 填寫後綴名爲 .htm → 填寫動作限制爲“GET,HEAD,POST,DEBUG”(爲方便省事你可選全部)

6、  最後點擊“確定” → 往項目中添加 HtmlPage1.htm → 在 IE 瀏覽器的地址欄直接輸入http://localhost/FormTest/HtmlPage1.htm → 觀看測試效果

最後送大家一段 Web.config 設置,發完睡覺,實在是困的不行了。

<location path ="決戰紫禁城">

            <system.web>

                <authorization>

<allow users="葉孤城"/>

<allow users="西門吹雪"/>

<deny users="*"/>

</authorization>

            </system.web>

       </location>

<location path ="金鑾殿屋脊">

            <system.web>

       <authorization>

<allow users="腰繫緞帶之人"/>

<deny users="*"/>

</authorization>

            </system.web>

       </location>

第三部分 實現單點登錄(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**
CITYHUNTER172@126*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**CITYHUNTER172@126*COM*****F
2F286CDCB15A9D1D4535E16DB0248026939" 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**CITYHUNTER172@126*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 程序 €€€€ 記事本 來開刀,保存文檔時得來的經驗。

所以服務器讀出來的格式如下圖:

5、 在 ASP.NET 頁面發放 Cookie

發送上述 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">

美麗的天使</a>

<a target ="_blank"  

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

快樂的天堂</a>

<a target ="_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.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. ,代碼偶就不寫,大家自個完成吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章