日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

Security Tutorials系列文章第三章:Forms Auth

系統(tǒng) 2141 0

本文英文原版及代碼下載: http://www.asp.net/learn/security/tutorial-03-cs.aspx

Security Tutorials系列文章第三章:Forms Authentication Configuration and Advanced Topics

導(dǎo)言:

在上一章,我們探討了在ASP.NET應(yīng)用程序了執(zhí)行forms authentication所必須的步驟,如何在Web.config文件里指定from配置以創(chuàng)建一個(gè)登錄頁面,對認(rèn)證用戶和匿名用戶分別顯示不同的內(nèi)容.記得我們通過將<authentication>元素的mode屬性配置為Forms以使站點(diǎn)使用forms authentication.其實(shí)<authentication>元素還可以任意的包含一個(gè)<forms>子元素,通過該子元素,我們可以指定各類forms authentication配置.

在本文,我們將考察各種不同的forms authentication設(shè)置,以及如何通過<forms>元素來對它們進(jìn)行修改.我們將詳細(xì)的考察如何定制表單驗(yàn)證票據(jù)的timeout值,如何為登錄頁指定一個(gè)自定義URL(比如用SignIn.aspx替換掉默認(rèn)的Login.aspx), 以及無cookie的表單認(rèn)證票據(jù).我們也將更加深入地考察表單驗(yàn)證票據(jù)的結(jié)構(gòu)(makeup),看ASP.NET如何來確保票據(jù)的數(shù)據(jù)的安全.我們也會(huì)考察如何在表單認(rèn)證票據(jù)里存儲額外的用戶數(shù)據(jù),以及如何通過一個(gè)自定義principal object對象來對數(shù)據(jù)進(jìn)行模型化(model this data).


第一步:考察<forms>配置選項(xiàng)

ASP.NET里的forms authentication system提供了一系列的配置選項(xiàng).比如表單驗(yàn)證票據(jù)的生命周期;對票據(jù)實(shí)施何種保護(hù);在何種條件下使用無cookie的票據(jù);登錄頁面的路徑等等.為了修改這些默認(rèn)的值,我們要在<authentication>元素里添加一個(gè)子元素<forms>,指定的屬性值要像XML屬性那樣,比如:

<authentication mode="Forms">
<forms propertyName1="value1"
propertyName2="value2"
...
propertyNameN="valueN" />
</authentication>

表1對可以通過<forms>元素進(jìn)行定制的屬性進(jìn)行了匯總,由于Web.config是一個(gè) XML文件, 在表左邊的attribute名稱都是區(qū)分大小寫的.

Table 1: <forms>元素屬性匯總

在ASP.NET 2.0及更高版本,默認(rèn)的forms authentication值都是在.NET Framework里的FormsAuthenticationConfiguration類里“硬編碼”的.任何的修改都應(yīng)在Web.config里依據(jù)application-by-application的原則進(jìn)行.這與ASP.NET 1.x不同,在ASP.NET 1.x里,默認(rèn)的forms authentication值是存儲在machine.config文件里的(因此可以通過machine.config來進(jìn)行修改).關(guān)于這一點(diǎn),有必要提一下,ASP.NET 1.x里的很多forms authentication配置與ASP.NET 2.0及更高版本是不同的.如果你打算將應(yīng)用程序從ASP.NET 1.x環(huán)境里遷移過來,知曉這些差異是很重要的.對這些差異,你可以查閱《the <forms> element technical documentation》.
注意:
有幾個(gè)forms authentication配置,比如timeout, domain, 以及path,都對最終的表單認(rèn)證票據(jù)cookie進(jìn)行了詳細(xì)的指定.關(guān)于cookies的更多信息,比如它們是如何進(jìn)行工作的,它包含的哪些屬性等,請查閱系列文章《Cookies tutorial》.


指定票據(jù)的Timeout值

表單認(rèn)證票據(jù)就是用戶身份的憑證(ticket).對基于cookie的票據(jù)而言,該憑證是以cookie的形式進(jìn)行存儲的,每次發(fā)出請求都會(huì)將該憑證發(fā)送到web server.從本質(zhì)上來說,擁有了憑證,就相當(dāng)于向系統(tǒng)宣稱:“我是xxx,我已經(jīng)登錄,通過認(rèn)證了”,在用戶在頁面間導(dǎo)航時(shí)記住用戶的身份(identity).

表單驗(yàn)證票據(jù)不僅包含了用戶的身份,還包含了其它的信息以確保憑證的真實(shí)性和安全性.畢竟,我們并不希望一個(gè)懷有惡意的用戶創(chuàng)建一個(gè)假憑證或?qū)δ硞€(gè)真實(shí)有效的憑證秘密的進(jìn)行修改.

票據(jù)包含的眾多信息里面有一個(gè)叫做expiry(可理解為有效期),它是票據(jù)過期的日期和時(shí)間點(diǎn).每次FormsAuthenticationModule對一個(gè)票據(jù)進(jìn)行檢查時(shí)都要確保其沒有過期,如果過期了就銷毀票據(jù),將用戶看作匿名用戶.這種安全措施可以抵御重發(fā)攻擊(replay attacks).沒有expiry的話,如果某個(gè)黑客截獲了一個(gè)有效的認(rèn)證票據(jù),它們就可以用這個(gè)偷來的票據(jù)向服務(wù)器發(fā)送一個(gè)請求,以通過認(rèn)證,達(dá)到登錄的目的.雖然expiry不能預(yù)防這種情況的發(fā)生,但它可以對這種攻擊可以成功的窗口進(jìn)行限制(it does limit the window during which such an attack can succeed.).
注意:
第3步將詳細(xì)介紹forms authentication用于保護(hù)票據(jù)的技術(shù).

當(dāng)創(chuàng)建票據(jù)后,forms authentication system通過參考timeout設(shè)置來判斷它的expiry.正如Table 1里提到的那樣,默認(rèn)設(shè)置是30分鐘,意思就是說,票據(jù)在其創(chuàng)建之時(shí)起的30分鐘內(nèi)有效.

這樣定義的是票據(jù)的絕對的過期時(shí)間,但開發(fā)人員通常希望執(zhí)行的是靈活的過期時(shí)間,也就是說,在每次用戶向站點(diǎn)發(fā)出請求時(shí),只要票據(jù)還沒有過期就重新設(shè)置過期時(shí)間.為此,我們要通過slidingExpiration配置選項(xiàng)來實(shí)現(xiàn)該行為.如果將它設(shè)置為true,每次FormsAuthenticationModule對用戶通過認(rèn)證后,就更新該用戶的票據(jù)的expiry.如果設(shè)置為false (默認(rèn)值), 就不對expiry進(jìn)行更新,后果就是,用戶票據(jù)一旦過了設(shè)置好的絕對過期點(diǎn),用戶就從認(rèn)證用戶變成匿名用戶.
注意:
票據(jù)的expiry信息是一個(gè)絕對的日期和時(shí)間值,比如“August 2, 2008 11:34 AM.” 然而,該日期和時(shí)間值又與服務(wù)器所在的本地時(shí)間有關(guān).我們假定服務(wù)器所在的區(qū)域采用的時(shí)間標(biāo)準(zhǔn)是Daylight Saving Time(DST)——也就是美國地區(qū)的時(shí)間要向前推進(jìn)一個(gè)小時(shí) (注:不知這樣翻譯是否恰當(dāng),地理知識忘得差不多了,翻譯有誤請指正!).我們來看看某個(gè)ASP.NET站點(diǎn)在DST執(zhí)行時(shí)間(也就是2:00 AM)左右會(huì)發(fā)生什么有趣的事情.假定某個(gè)用戶在March 11, 2008 1:55 AM登錄網(wǎng)站,那么站點(diǎn)將會(huì)為他創(chuàng)建一個(gè)票據(jù),該票據(jù)過期時(shí)間為March 11, 2008 2:25 AM(因?yàn)椴捎玫氖悄J(rèn)的30分鐘).然而,到了2:00 AM,因?yàn)椴捎玫氖荄ST時(shí)間標(biāo)準(zhǔn)的緣故,時(shí)間自動(dòng)跳到3:00 AM.當(dāng)用戶在登錄6分鐘后(也就是在3:01 AM)訪問一個(gè)新頁面時(shí),F(xiàn)ormsAuthenticationModule發(fā)現(xiàn)票據(jù)已經(jīng)過期了,因此將用戶重新導(dǎo)航到登錄頁面.關(guān)于這點(diǎn)以及票據(jù)的timeout的稀奇古怪的事情的探討,請參閱Stefan Schackow所著的《Professional ASP.NET 2.0 Security, Membership, and Role Management》(ISBN: 978-0-7645-9698-8).


圖1闡述的是將slidingExpiration設(shè)置為false,且timeout為30時(shí)的工作流程.注意,登錄是創(chuàng)建的票據(jù)包含了票據(jù)的有效期,且在以后的請求發(fā)生時(shí)不對有效期進(jìn)行更新.如果FormsAuthenticationModule發(fā)現(xiàn)票據(jù)過期了就摒棄它,并將用戶當(dāng)成匿名用戶.

Security Tutorials系列文章第三章:Forms Authentication Configuration and Advanced Topics

圖1


圖2顯示的是slidingExpiration設(shè)置為true,且timeout設(shè)置為30時(shí)的工作流程.當(dāng)一個(gè)認(rèn)證用戶的請求抵達(dá)時(shí)(且該用戶的票據(jù)未過期),就對過期時(shí)間進(jìn)行更新.

Security Tutorials系列文章第三章:Forms Authentication Configuration and Advanced Topics
圖2

當(dāng)使用基于cookie的票據(jù)時(shí)(這是默認(rèn)的),問題就變的稍微復(fù)雜了點(diǎn),因?yàn)閏ookie也有自己的expiry,它指示瀏覽器該在什么時(shí)候銷毀cookie.

如果沒有為cookie指定expiry,在關(guān)閉瀏覽器時(shí)就會(huì)自動(dòng)銷毀cookie.如果有expiry,cookie就存儲在用戶的電腦里,超過指定的日期和時(shí)間時(shí)就會(huì)被銷毀.當(dāng)某個(gè)cookie被銷毀掉了,就再也不會(huì)將它發(fā)送給服務(wù)器了.
注意:
當(dāng)然,我們也可以提前銷毀存儲在電腦里的任何cookies.在Internet Explorer 7里,你點(diǎn)“工具”、“選項(xiàng)”、“瀏覽器歷史記錄”區(qū)域里的“刪除”按鈕,再點(diǎn)“刪除cookies”按鈕即可.

forms authentication system是創(chuàng)建基于session還是基于expiry(session-based or expiry-based)的cookies 呢?這要取決于傳遞給persistCookie參數(shù)的值.在前一篇文章里,我們提到FormsAuthentication類的GetAuthCookie(), SetAuthCookie(),和RedirectFromLoginPage()方法都包含2個(gè)參數(shù):username和 persistCookie.另外,我們創(chuàng)建的登錄頁面包含一個(gè)“Remember me” CheckBox,通過它來判斷是否創(chuàng)建一個(gè)“持久保存的cookie”(Persistent cookies).“持久保存的cookie”是基于expiry的;而“非持久保存的cookie”是基于session的.

不管是基于session還是基于expiry的cookies,前面探討的timeout 和 slidingExpiration概念應(yīng)用到這2種cookies時(shí)都是一樣的.唯一比較大的區(qū)別在于執(zhí)行方面:對使用基于expiry的cookies而言,如果將slidingTimeout設(shè)置為true的話,當(dāng)指定的有效時(shí)間長度超過一半時(shí)(比如,如果指定的有效期為20分鐘,當(dāng)時(shí)間超過10分?jǐn)?shù)時(shí))才對cookie的expiry進(jìn)行更新.

我們來對站點(diǎn)的票據(jù)過期策略進(jìn)行改動(dòng),使票據(jù)在一個(gè)小時(shí)(60分鐘)后過期,且使用sliding expiration.為此,改動(dòng)Web.config文件,為

<authentication>元素添加一個(gè)<forms>子元素,設(shè)置如下:
<authentication mode="Forms">
<forms slidingExpiration="true" timeout="60" /> </authentication>


使用定制的登錄頁面URL,而不是默認(rèn)的Login.aspx

由于FormsAuthenticationModule自動(dòng)將未授權(quán)的用戶導(dǎo)航到登錄頁面,因此它需要知道登錄頁面的URL.該URL通過<forms>元素的loginUrl屬性來指定,默認(rèn)值為“l(fā)ogin.aspx”.

比如你的系統(tǒng)的登錄頁面為SignIn.aspx,且位于Users目錄下,那么你可以將 loginUrl設(shè)置為“~/Users/SignIn.aspx”,如下:

<authentication mode="Forms">
<forms loginUrl="~/Users/SignIn.aspx" />
</authentication>

當(dāng)然,如果你的系統(tǒng)的登錄頁面就是Login.aspx,那就用不著在<forms>元素里進(jìn)行指定了.


第二步:使用無Cookie的表單認(rèn)證票據(jù)

默認(rèn)情況下,forms authentication system將決定是將票據(jù)存儲在cookies collection里還是插入用戶訪問頁面的URL里.所有主流的桌面瀏覽器,比如Internet Explorer,F(xiàn)irefox, Opera, 或Safari都支持cookies,但并非所有的移動(dòng)設(shè)備都支持.

forms authentication system使用何種cookie策略,取決于<forms>元素里的cookieless設(shè)置,它可以有如下四種配置:

.UseCookies——指定總是使用基于cookie的票據(jù)

.UseUri——指定從不使用基于cookie的票據(jù)

.AutoDetect——如果device profile不支持cookies,就不使用基于cookie的票據(jù);如果device profile支持cookies,那么就運(yùn)用一種探測機(jī)制來判斷是否可以使用cookies.

.UseDeviceProfile——這是默認(rèn)值.如果device profile支持cookies,就使用基于cookie的票據(jù).不運(yùn)用探測機(jī)制.

其中,AutoDetect 和 UseDeviceProfile選項(xiàng)都依靠一個(gè)device profile來判斷是使用基于cookie還是無cookie的票據(jù).ASP.NET有一個(gè)關(guān)于這種devices及其性能的數(shù)據(jù)庫,比如某種devices是否支持cookies,它支持那個(gè)版本的JavaScript等信息.每次,當(dāng)一個(gè)device向服務(wù)器發(fā)出對某個(gè)頁面的請求時(shí),該請求里將包含一
個(gè)名為“user-agent”的HTTP header,用于表明device的類型.ASP.NET將自動(dòng)的把提供的user-agent字符串與數(shù)據(jù)庫里相應(yīng)的信息匹配起來.
注意:
該數(shù)據(jù)庫存儲在很多的XML文件里.這些默認(rèn)的device profile文件,其路徑為%WINDIR%\Microsoft.Net\Framework\v2.0.50727\CONFIG\Browsers. 你也可以在你應(yīng)用程序的App_Browsers文件夾里添加自定義的文件,關(guān)于這方面的更多信息,請參閱文章《How To: Detect Browser Types in ASP.NET Web Pages》

由于默認(rèn)使用的是UseDeviceProfile選項(xiàng).當(dāng)訪問站點(diǎn)的某個(gè)device不支持cookies時(shí),就站點(diǎn)就使用無cookie的票據(jù).


在URL里對票據(jù)進(jìn)行編碼

當(dāng)瀏覽器每次向某個(gè)站點(diǎn)發(fā)出請求時(shí),用來存儲信息的載體通常是Cookies.但如果訪問站點(diǎn)的device不支持Cookies的話,我們必須使用某種方法在客戶端和服務(wù)器端傳遞票據(jù),通常的做法是在URL里將cookie數(shù)據(jù)編碼.

為了進(jìn)行演示,我們將強(qiáng)迫站點(diǎn)使用無cookie的票據(jù),為此我們將采用UseUri:

<authentication mode="Forms">
<forms cookieless="UseUri" slidingExpiration="true" timeout="60"/>
</authentication>

做了上述修改后,通過瀏覽器訪問.當(dāng)以匿名用戶進(jìn)行訪問時(shí),URL看起來和以前沒什么區(qū)別,比如訪問Default.aspx頁面時(shí),地址欄看起來和下面的差不多:

http://localhost:2448/ASPNET_Security_Tutorial_03_CS/default.aspx

然而一旦你登錄后,票據(jù)將加密到URL里.比如,當(dāng)以Sam的名義登錄后,轉(zhuǎn)到Default.aspx頁面,這次地址欄看起來和下面的差不多:

http://localhost:2448/ASPNET_Security_Tutorial_03_CS/(F(jaIOIDTJxIr12xYS-VVgkqKCVAuIoW30Bu0diWi6flQC-FyMaLXJfow_Vd9GZkB2Cv-rfezq0gKadKX0YPZCkA2))/default.aspx

該票據(jù)已經(jīng)被編碼進(jìn)URL。字符串(F(jaIOIDTJxIr12xYS-VVgkqKCVAuIoW30Bu0diWi6flQC-FyMaLXJfow_Vd9GZkB2Cv-rfezq0gKadKX0YPZCkA2)就是以16進(jìn)制對票據(jù)信息編碼后的效果.這于通常情況下存儲在一個(gè)cookie里的數(shù)據(jù)是一樣的.

為使無cookie的票據(jù)工作正常,系統(tǒng)必須對所有頁面的URL編碼以包含票據(jù)數(shù)據(jù).另外,當(dāng)用戶點(diǎn)擊一個(gè)鏈接時(shí),票據(jù)也不會(huì)丟失.還好,該編碼過程是自動(dòng)執(zhí)行的.為演示該功能,我們打開Default.aspx頁面,添加一個(gè)HyperLink鏈接,分別設(shè)置其Text 和 NavigateUrl屬性為“Test Link”和“SomePage.aspx”, 當(dāng)然,這并不是說我們的站點(diǎn)真的有個(gè)頁面叫SomePage.aspx.

保存對Default.aspx的改動(dòng),再從瀏覽器訪問它.先登錄,以便把票據(jù)編碼進(jìn)URL,然后,在Default.aspx頁面,點(diǎn)擊“Test Link”鏈接.會(huì)發(fā)生什么?如果不存在SomePage.aspx頁面,就會(huì)發(fā)生一個(gè)404錯(cuò)誤,不過在這里這并不重要,注意觀察地址欄,在URL里包含了該票據(jù)!

鏈接里的URL——“SomePage.aspx”,將自動(dòng)的添加到一個(gè)包含票據(jù)的一個(gè)URL里——我們不用手寫一行代碼!票據(jù)將自動(dòng)的編碼進(jìn)一個(gè)URL,只是該URL不能以 “http://”或“/”開頭. 至于該鏈接是出現(xiàn)在一個(gè)對Response.Redirect的調(diào)用,或一個(gè)HyperLink控件里,又或是一個(gè)anchor HTML元素(比如<a href="...">...</a>)里,那到無關(guān)緊要.只要URL不是“http://www.someserver.com/SomePage.aspx” 或 “/SomePage.aspx”這樣的形式,系統(tǒng)都會(huì)為我們自動(dòng)編碼.
注意:
雖然和基于cookie的票據(jù)一樣,無cookie的票據(jù)也采取了某種過期策略,但無cookie的票據(jù)卻更用戶受到replay攻擊,原因正是因?yàn)槠睋?jù)被編碼進(jìn)了URL.我們來設(shè)想一下,加入某個(gè)用戶訪問站點(diǎn),成功登錄后將URL復(fù)制下來通過電郵傳給他的同事,在票據(jù)未過期之前,他的同事點(diǎn)擊該鏈接,那么他們都能以那個(gè)發(fā)郵件的用戶的名義登錄系統(tǒng)了!


第三步:Securing the Authentication Ticket

除了身份信息外,票據(jù)還可以包含用戶數(shù)據(jù)(我們將在第四步看到),因此將票據(jù)進(jìn)行加密很有必要,更重要的是,forms authentication system必須保證票據(jù)未被篡改過.

為確保票據(jù)的真實(shí)性,forms authentication system必須驗(yàn)證票據(jù).驗(yàn)證就是這樣的行為:確保某個(gè)數(shù)據(jù)塊沒有被修改過,這是通過message authentication code (MAC)來實(shí)現(xiàn)的. MAC就是一小片信息,用來對需要進(jìn)行驗(yàn)證的數(shù)據(jù)(就本文而言,就是票據(jù))實(shí)施鑒別.如果數(shù)據(jù)被改動(dòng)過,那么MAC就不能與改動(dòng)過的數(shù)據(jù)匹配.另外,對某個(gè)黑客來說,既要改動(dòng)數(shù)據(jù),還要計(jì)算產(chǎn)生一個(gè)自己的MAC,以便使該MAC與改動(dòng)過的數(shù)據(jù)匹配實(shí)在是太困難了.

當(dāng)forms authentication system創(chuàng)建或改動(dòng)一個(gè)票據(jù)后,就生成一個(gè)與票據(jù)數(shù)據(jù)相對應(yīng)的MAC.當(dāng)后續(xù)的請求到達(dá)時(shí),forms authentication system就將MAC與票據(jù)數(shù)據(jù)進(jìn)行比較,以驗(yàn)證票據(jù)數(shù)據(jù)的真實(shí)性.圖3以圖表的形式對該工作流程進(jìn)行了闡述.

Security Tutorials系列文章第三章:Forms Authentication Configuration and Advanced Topics
圖3


對票據(jù)運(yùn)用何種安全措施取決于<forms>元素里protection的配置.該項(xiàng)的值可為如下幾個(gè)值之一:

.All——默認(rèn)值,票據(jù)要加密且運(yùn)用數(shù)據(jù)有效性驗(yàn)證

.Encryption——只加密,不生成MAC

.None——既不加密也不運(yùn)用數(shù)據(jù)有效性驗(yàn)證.

.Validation——生成一個(gè)MAC,但不對票據(jù)加密,以純文本的形式傳遞.

微軟強(qiáng)烈推薦使用All配置選項(xiàng).


Setting the Validation and Decryption Keys

forms authentication system運(yùn)用的加密方法以及驗(yàn)證票據(jù)的散列法可以在Web.config文件的<machineKey>元素里進(jìn)行用戶定制.Table 2列出了<machineKey>元素的屬性以及可能的取值.

Security Tutorials系列文章第三章:Forms Authentication Configuration and Advanced Topics
Table 2: <machineKey>元素屬性

對這些encryption 和 validation選項(xiàng)的深入研究,以及各種算法的優(yōu)缺點(diǎn)探討已經(jīng)超出了本系列文章的范疇,對這些問題的深入探討,包括使用哪種encryption 和 validation算法,密匙的長度,如何最好的生成這些keys,請參閱《Professional ASP.NET 2.0 Security, Membership, and Role Management》.

默認(rèn)情況下,會(huì)自動(dòng)為每個(gè)應(yīng)用程序生成用于encryption和validation的keys,這些keys都存儲在Local Security Authority (LSA). 簡而言之,根據(jù)server-by-web server 和 application-by-application準(zhǔn)則,默認(rèn)配置保證keys都是唯一的、獨(dú)特的.因此,在這種默認(rèn)行為在如下2種情況下是不行的:

.Web Farms——在一個(gè)web farm里,一個(gè)應(yīng)用程序運(yùn)行在多個(gè)服務(wù)器上,每個(gè)后續(xù)的請求都會(huì)指派到場中的一個(gè)服務(wù)器處理,這就意味著在一個(gè)用戶的會(huì)話期間,每個(gè)服務(wù)器都可能要處理他的請求.因此,每個(gè)服務(wù)器都必須使用相同的encryption 和 validation keys,這樣,在一臺服務(wù)器上created, encrypted, 以及validated的票據(jù)才能在另一臺服務(wù)器上進(jìn)行decrypted和validated操作.

.Cross Application Ticket Sharing——一個(gè)服務(wù)器上可能運(yùn)行了多個(gè)ASP.NET運(yùn)用程序.如果你需要讓不同的應(yīng)用程序之間共用一個(gè)單獨(dú)的票據(jù),你必須使這些運(yùn)用程序的encryption 和 validation keys匹配.

當(dāng)你處于上述2種情況之一時(shí),你必須在受影響的應(yīng)用程序里配置<machineKey>元素,保證這些程序的decryptionKey 和 validationKey相互匹配.

即使你的應(yīng)用程序沒有處于上述2種情況下,你也可以顯式的指定decryptionKey 和 validationKey值,以及定義要運(yùn)用的運(yùn)算法則.在Web.config文件里添加一個(gè)<machineKey>配置項(xiàng):

<configuration>
<system.web> ... Some markup was removed for brevity ...

<machineKey decryption="AES" validation="SHA1" decryptionKey="1513F567EE75F7FB5AC0AC4D79E1D9F25430E3E2F1BCDD3370BCFC4EFC97A541" validationKey="32CBA563F26041EE5B5FE9581076C40618DCC1218F5F447634EDE8624508A129" />
</system.web>
</configuration>

更多詳情,請參閱《How To: Configure MachineKey in ASP.NET 2.0》
注意:
上面運(yùn)用的這些decryptionKey 和 validationKey值,采用的是Steve Gibson的《Perfect Passwords web page》,詳情請參閱該文.


第四步:在票據(jù)里存儲附加的用戶數(shù)據(jù)

很多應(yīng)用程序顯示當(dāng)前登錄用戶的某些信息,比如用戶名或最后一次登錄的時(shí)間.票據(jù)存儲了當(dāng)前用戶的名稱,但如果需要其它的信息時(shí),頁面必須求助于用戶存儲——一般是某個(gè)數(shù)據(jù)庫——以查找需要的,但又沒有存儲在票據(jù)里的信息.

我們只需要很少的代碼就可以在票據(jù)里存儲附加的用戶信息.我們要用到FormsAuthenticationTicket類的UserData屬性.該屬性是存儲與用戶相關(guān)的數(shù)據(jù)量不大的信息的理想之地.對該屬性指定的值可以是authentication ticket cookie.根據(jù)forms authentication system的配置來進(jìn)行加密和驗(yàn)證.默認(rèn)情況下,該UserData是一個(gè)空字符串.

為了在票據(jù)里存儲用戶數(shù)據(jù),我們需要在登錄頁面寫代碼以獲取用戶信息并存儲進(jìn)票據(jù).由于UserData是一個(gè)字符串類型的屬性,因此存儲在該屬性里的數(shù)據(jù)必須要序列化為一個(gè)字符串.比如,假定我們的用戶存儲包括每個(gè)用戶的出生日期和所在公司名稱,我們希望將這2個(gè)屬性存儲在票據(jù)里.我們可以用(“|”)來將這2個(gè)屬性連接起來,比如某個(gè)用戶出生于August 15, 1974,所在公司為Northwind Traders,那么我們可以對UserData屬性賦值為這樣的字符串:“1974-08-15|Northwind Traders”.

任何時(shí)候當(dāng)我們需要訪問存儲在票據(jù)里的數(shù)據(jù)時(shí),我們可以獲取當(dāng)前請求的FormsAuthenticationTicket,并對UserData屬性進(jìn)行反序列化(deserializing).以上面的例子來說,我們可以根據(jù)分隔符(“|”)將UserData字符串分割為2個(gè)子字符串.

Security Tutorials系列文章第三章:Forms Authentication Configuration and Advanced Topics
圖4:將用戶信息存儲在票據(jù)里


將信息寫入U(xiǎn)serData

不幸的是,將用戶信息寫入票據(jù)并不如大家期望的那么簡單.FormsAuthenticationTicket類的UserData屬性是只讀的,只能通過FormsAuthenticationTicket類的構(gòu)造器進(jìn)行指定.當(dāng)我們在構(gòu)造器里為該屬性指定值時(shí),我們還需要提供票據(jù)的其它值,比如:username,issue date, expiration等等.在前面的文章里,當(dāng)我們創(chuàng)建登錄頁面時(shí),F(xiàn)ormsAuthentication類以及自動(dòng)的幫我們進(jìn)行了處理.要在票據(jù)里添加用戶數(shù)據(jù)時(shí),我們要重新用到FormsAuthentication類里提供的、已有的函數(shù).

讓我們對Login.aspx頁面進(jìn)行更新,以向票據(jù)添加額外的信息,這樣來探究處理UserData所必需的代碼.假設(shè)我們的用戶存儲包含用戶公司的名稱,以及用戶的頭銜,而且我們想在票據(jù)里獲取這些信息.為此,對Login.aspx頁面的LoginButton Click事件處理器進(jìn)行更新,如下:

protected void LoginButton_Click(object sender, EventArgs e)
{
// Three valid username/password pairs: Scott/password, Jisun/password, and Sam/password.

string[] users = { "Scott", "Jisun", "Sam" };
string[] passwords = { "password", "password", "password" };
string[] companyName = { "Northwind Traders", "Adventure Works", "Contoso" };
string[] titleAtCompany = { "Janitor", "Scientist", "Mascot" };

for (int i = 0; i < users.Length; i++)
{
bool validUsername = (string.Compare(UerName.Text, users[i], true) == 0);
bool validPassword = (string.Compare(Password.Text, passwords[i], false) == 0);
if (validUsername && validPassword)
{
// Query the user store to get this user's User Data
string userDataString = string.Concat(companyName[i], "|", titleAtCompany[i]);

// Create the cookie that contains the forms authentication ticket
HttpCookie authCookie = FormsAuthentication.GetAuthCookie(UserName.Text, RememberMe.Checked);
// Get the FormsAuthenticationTicket out of the encrypted cookie
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(authCookie.Value);

// Create a new FormsAuthenticationTicket that includes our custom User Data
FormsAuthenticationTicket newTicket = new FormsAuthenticationTicket(ticket.Version, ticket.Name, ticket.IssueDate, ticket.Expiration, ticket.IsPersistent, userDataString);

// Update the authCookie's Value to use the encrypted version of newTicket
authCookie.Value = FormsAuthentication.Encrypt(newTicket);

// Manually add the authCookie to the Cookies collection
Response.Cookies.Add(authCookie);

// Determine redirect URL and send user there
string redirUrl = FormsAuthentication.GetRedirectUrl(UserName.Text, RememberMe.Checked);

Response.Redirect(redirUrl);
}
}
// If we reach here, the user's credentials were invalid
InvalidCredentialsMessage.Visible = true;
}

我們一行一行的進(jìn)行分析.該方法首先定義了4個(gè)字符串?dāng)?shù)組:users, passwords, companyName,和titleAtCompany.這些數(shù)組用于存儲用戶名、密碼、公司名稱、以及用戶頭銜.這里有3個(gè)用戶Scott, Jisun,Sam.在實(shí)際的應(yīng)用程序里,這些值都是從用戶存儲里查詢得到的,不像我們這樣在頁面的源代碼里"硬編碼"得到的.

在前面的文章里,如果用戶提供的登錄信息有誤,我們僅僅調(diào)用FormsAuthentication.RedirectFromLoginPage(UserName.Text, RememberMe.Checked)方法,它執(zhí)行如下的步驟:

1.創(chuàng)建表單驗(yàn)證票據(jù)

2.將票據(jù)寫入適當(dāng)?shù)拇鎯?對基于cookies的票據(jù)而言,使用的是瀏覽器的cookies collection;而對無cookies的票據(jù)而言,將票據(jù)數(shù)據(jù)序列化進(jìn)URL

3.將用戶重導(dǎo)航到某個(gè)恰當(dāng)?shù)捻撁?

這些步驟在上述代碼里都實(shí)現(xiàn)了.首先,我們用(“|”)將company name和title連接起來,作為存儲到UserData屬性里的最終字符串,如下:

string userDataString = string.Concat(companyName[i], "|", titleAtCompany[i]);

接下來,調(diào)用FormsAuthentication.GetAuthCookie方法,它創(chuàng)建票據(jù),根據(jù)配置的情況進(jìn)行encrypts 和 validates處理,并賦值給一個(gè)HttpCookie對象:

HttpCookie authCookie = FormsAuthentication.GetAuthCookie(UserName.Text, RememberMe.Checked);

為了處理植入cookie里的FormAuthenticationTicket,我們需要調(diào)用FormAuthentication類的Decrypt方法, 傳入cookie值,如下:

FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(authCookie.Value);

然后我們在現(xiàn)有FormsAuthenticationTicket的值的基礎(chǔ)上,創(chuàng)建一個(gè)新的FormsAuthenticationTicket實(shí)例,該新票據(jù)包含與用戶有關(guān)的信息(也就是userDataString),如下:

FormsAuthenticationTicket newTicket = new FormsAuthenticationTicket(ticket.Version, ticket.Name, ticket.IssueDate, ticket.Expiration, ticket.IsPersistent, userDataString);

當(dāng)調(diào)用Encrypt方法對新票據(jù)進(jìn)行encrypt (以及validate)處理后,又將其返回給authCookie,如下:

authCookie.Value = FormsAuthentication.Encrypt(newTicket);

最后將authCookie添加到Response.Cookies集合,并調(diào)用GetRedirectUrl方法將用戶導(dǎo)航到恰當(dāng)?shù)捻撁?

Response.Cookies.Add(authCookie);

string redirUrl = FormsAuthentication.GetRedirectUrl(UserName.Text, RememberMe.Checked);

Response.Redirect(redirUrl);

所有這些代碼都是必須的,因?yàn)閁serData屬性是只讀的,且在FormsAuthentication類的GetAuthCookie, SetAuthCookie,或 RedirectFromLoginPage方法里都沒有提供任何指定UserData信息的途徑.
注意:
以上代碼將用戶相關(guān)的信息存儲進(jìn)基于cookies的票據(jù).另一方面,將票據(jù)序列化到URL的工作是由.NET Framework內(nèi)部的類進(jìn)行處理的,因此,對無cookie的票據(jù)而言,我們無法將用戶數(shù)據(jù)存儲到票據(jù)里.


訪問UserData信息

至此,當(dāng)用戶登錄時(shí),用戶的公司名以及他們的頭銜都存儲在票據(jù)的UserData屬性里.在任何一個(gè)頁面上,不用再去查詢用戶存儲,我們就可以通過票據(jù)訪問這些信息.為進(jìn)行演示,我們對Default.aspx頁面進(jìn)行更新,使歡迎信息里不但包含用戶名,還包含公司名和他的頭銜.

目前,Default.aspx頁面包含一個(gè)名為AuthenticatedMessagePanel的Panel控件,里面有一個(gè)名為WelcomeBackMessage的Label控件.該P(yáng)anel是面向認(rèn)證用戶的.對Default.aspx頁面的Page_Load事件處理器進(jìn)行更新,如下:
protected void Page_Load(object sender, EventArgs e)
{
if (Request.IsAuthenticated)
{ WelcomeBackMessage.Text = "Welcome back, " + User.Identity.Name + "!";
// Get User Data from FormsAuthenticationTicket and show it in WelcomeBackMessage
FormsIdentity ident = User.Identity as FormsIdentity;
if (ident != null)
{ FormsAuthenticationTicket ticket = ident.Ticket;
string userDataString = ticket.UserData;
// Split on the |
string[] userDataPieces = userDataString.Split("|".ToCharArray());
string companyName = userDataPieces[0];
string titleAtCompany = userDataPieces[1];
WelcomeBackMessage.Text += string.Format(" You are the {0} of {1}.", titleAtCompany, companyName);
}
AuthenticatedMessagePanel.Visible = true;
AnonymousMessagePanel.Visible = false; }
else
{ AuthenticatedMessagePanel.Visible = false;
AnonymousMessagePanel.Visible = true; }
}


如果Request.IsAuthenticated為true,則將WelcomeBackMessage的Text屬性設(shè)置為“Welcome back, username.” ,然后將User.Identity屬性轉(zhuǎn)換為一個(gè)FormsIdentity對象,以便于我們訪問潛在的FormsAuthenticationTicket.一旦我們獲取到FormsAuthenticationTicket后,我們根據(jù)分割符"|",將UserData屬性反序列化為2個(gè)代表公司和頭銜的字符串.然后將公司名和頭銜顯示在WelcomeBackMessage Label里.

圖5顯示的是實(shí)際的一個(gè)截屏,以Scott的名義登錄,歡迎消息里包含Scott的公司名和頭銜.

Security Tutorials系列文章第三章:Forms Authentication Configuration and Advanced Topics

圖5:

注意:
票據(jù)的UserData屬性是用戶存儲的一個(gè)緩存.與其它任何緩存一樣,當(dāng)“源數(shù)據(jù)”發(fā)生改動(dòng)時(shí),需要對緩存進(jìn)行刷新.


第五步:Using a Custom Principal

每次請求抵達(dá)時(shí),F(xiàn)ormsAuthenticationModule都會(huì)對用戶進(jìn)行鑒別.如果票據(jù)未過期,F(xiàn)ormsAuthenticationModule就將HttpContext.User屬性賦值為一個(gè)新的 該GenericPrincipal對象有一個(gè)代表表單認(rèn)證票據(jù)FormsIdentity.而GenericPrincipal類包含的功能僅僅夠貫徹IPrincipal接口——它只有一個(gè)Identity屬性和一個(gè)IsInRole方法.

principal對象有2個(gè)職責(zé):指出用戶屬于什么角色以及提供identity信息.這2個(gè)職責(zé)是分別通過IPrincipal接口的IsInRole(roleName)方法和Identity屬性來實(shí)現(xiàn)的.GenericPrincipal類允許通過它的構(gòu)造器為role名稱指定一個(gè)字符串?dāng)?shù)組,而其IsInRole(roleName)方法僅僅檢查傳入的roleName是否存在于該字符串?dāng)?shù)組里.當(dāng)FormsAuthenticationModule創(chuàng)建GenericPrincipal時(shí),在GenericPrincipal的構(gòu)造器里傳入一個(gè)空的字符串?dāng)?shù)組,因此當(dāng)調(diào)用IsInRole時(shí),總是返回false.

對絕大多數(shù)沒有用到role的、基于表單的認(rèn)證而言,該GenericPrincipal類是完全滿足需要的.如果這種對role的默認(rèn)處理無法滿足你的需要,或者你需要將用戶與一個(gè)自定義IIdentity對象聯(lián)系起來的話,你可以在認(rèn)證流程里創(chuàng)建一個(gè)自定義的IPrincipal對象,并將其賦值給HttpContext.User屬性.
注意:
正如你將在以后的文章看到的那樣,當(dāng)使ASP.NET’s Roles framework創(chuàng)建一個(gè)類型為RolePrincipal的自定義principal對象,并重寫forms authentication創(chuàng)建GenericPrincipal對象的方法時(shí),我們必須這樣做,對principal的IsInRole方法進(jìn)行定制,以便與Roles framework的API“接軌”.

由于我們現(xiàn)在還沒有牽涉到role,因此本文只探討自定義IIdentity對象的問題.在第四步,我們考察了將附加的用戶信息存儲在票據(jù)的UserData屬性里,具體來說就是公司名和員工的頭銜.然而,UserData信息只能通過票據(jù)進(jìn)行訪問,且是一個(gè)連續(xù)的字符串,這就意味著我們想訪問存儲在票據(jù)里的用戶信息時(shí),必須對UserData屬性進(jìn)行解析.

你還可以創(chuàng)建一個(gè)貫徹IIdentity接口的類,使它包含CompanyName屬性和Title屬性.那樣的話,開發(fā)人員就可以直接通過CompanyName 和 Title屬性直接訪問當(dāng)前登錄用戶的公司名稱和頭銜,而不用知道如何對UserData屬性進(jìn)行解析了.


創(chuàng)建自定義Identity 和 Principal類

本文,我們來在App_Code文件夾里創(chuàng)建自定義的principal和identity對象.首先在項(xiàng)目里添加一個(gè)App_Code文件夾,該文件夾專門用于存放專用于本站點(diǎn)的class文件.
注意:
只有當(dāng)通過Website Project Model對項(xiàng)目進(jìn)行管理時(shí)才應(yīng)使用App_Code文件夾.如果你使用的是Web Application Project Model, 你只需要?jiǎng)?chuàng)建一個(gè)一般的文件夾,將class文件放進(jìn)去即可.比如,你創(chuàng)建一個(gè)名為Classes的文件夾,以存放你的類.然后,向App_Code文件夾里添加2個(gè)class文件,一個(gè)叫CustomIdentity.cs,另一個(gè)叫CustomPrincipal.cs.

Security Tutorials系列文章第三章:Forms Authentication Configuration and Advanced Topics
圖6:向你的項(xiàng)目添加CustomIdentity 和 CustomPrincipal類


該CustomIdentity類用于執(zhí)行IIdentity接口,而IIdentity接口又定義了AuthenticationType, IsAuthenticated,以及Name屬性.除了這些必需的屬性外,我們還對表示票據(jù)里的用戶公司名和頭銜的屬性感興趣.在CustomIdentity類里鍵入如下的代碼:

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public class CustomIdentity : System.Security.Principal.IIdentity
{
private FormsAuthenticationTicket _ticket;

public CustomIdentity(FormsAuthenticationTicket ticket)
{
_ticket = ticket;
}

public string AuthenticationType
{
get { return "Custom"; }
}

public bool IsAuthenticated
{
get { return true; }
}

public string Name
{
get { return _ticket.Name; }
}

public FormsAuthenticationTicket Ticket
{
get { return _ticket; }
}

public string CompanyName
{
get {
string[] userDataPieces = _ticket.UserData.Split("|".ToCharArray());
return userDataPieces[0]; }
}

public string Title
{
get
{
string[] userDataPieces = _ticket.UserData.Split("|".ToCharArray());
return userDataPieces[1];
}
}

}

注意該類里包含一個(gè)FormsAuthenticationTicket類型的成員變量(_ticket),通過構(gòu)造器來提供票據(jù)信息.票據(jù)數(shù)據(jù)用于返回identity的Name;其UserData屬性經(jīng)過解析以返回CompanyName 和 Title屬性的值.

接下來創(chuàng)建CustomPrincipal類,因?yàn)樵谶@里我們暫時(shí)不關(guān)心role的問題,因此該CustomPrincipal類的構(gòu)造器僅僅接受一個(gè)CustomIdentity對象,其IsInRole方法總是返回false,如下:

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public class CustomPrincipal : System.Security.Principal.IPrincipal
{
private CustomIdentity _identity;

public CustomPrincipal(CustomIdentity identity)
{
_identity = identity;
}

public System.Security.Principal.IIdentity Identity
{
get { return _identity; }
}

public bool IsInRole(string role)
{
return false; }
}


將一個(gè)CustomPrincipal對象賦值給隨后請求的安全內(nèi)容

到目前為止,我們有一個(gè)對默認(rèn)的IIdentity接口進(jìn)行擴(kuò)展的類,它還包含CompanyName和Title屬性;也有一個(gè)使用我們自定義identity的自定義principal類.我們現(xiàn)在將深入ASP.NET,將我們自定義的principal對象賦值給隨后的請求的安全內(nèi)容里.

ASP.NET接到一個(gè)請求后,通過一系列的步驟對請求進(jìn)行處理.在每一步都會(huì)觸發(fā)一個(gè)具體的事件,這就為開發(fā)人員楔入ASP.NET處理通道內(nèi)部,在其生命周期的某一點(diǎn)上對請求進(jìn)行修改提供了可能.以FormsAuthenticationModule為例,當(dāng)ASP.NET引發(fā)AuthenticateReques事件后,在該事件里它對請求進(jìn)行認(rèn)證票據(jù)檢查,如果在請求里發(fā)現(xiàn)了票據(jù),就生成一個(gè)GenericPrincipal對象,并賦值給HttpContext.User屬性.

在AuthenticateRequest事件之后,ASP.NET又引發(fā)PostAuthenticateRequest事件, 在該事件里,我們可以將FormsAuthenticationModule創(chuàng)建的GenericPrincipal對象替換為我們自定義的CustomPrincipal對象的一個(gè)實(shí)例,圖7描繪了該流程.

Security Tutorials系列文章第三章:Forms Authentication Configuration and Advanced Topics
圖7


為了對ASP.NET的這些事件做出回應(yīng),我們要么在Global.asax文件里創(chuàng)建相應(yīng)的事件處理器,要么創(chuàng)建我們自己的HTTP Module,就本系列文章而言,我們在Global.asax文件里創(chuàng)建事件處理器.首先將Global.asax文件添加到站點(diǎn).

Security Tutorials系列文章第三章:Forms Authentication Configuration and Advanced Topics
圖8:在站點(diǎn)里添加一個(gè)Global.asax文件


默認(rèn)的Global.asax模板里包含了對應(yīng)于一些ASP.NET事件的事件處理器,包括Start, End以及Error事件等.放心大膽的將這些事件處理器刪除,因?yàn)槲覀兊膽?yīng)用程序里不需要用到它們,我們關(guān)心的事件是PostAuthenticateRequest.對你的Global.asax文件進(jìn)行更新,使其代碼看起來和下面的差不多:

<%@ Application Language="C#" %>
<%@ Import Namespace="System.Security.Principal" %>
<%@ Import Namespace="System.Threading" %>
<script runat="server">
void Application_OnPostAuthenticateRequest(object sender, EventArgs e)
{
// Get a reference to the current User
IPrincipal usr = HttpContext.Current.User;

// If we are dealing with an authenticated forms authentication request
if (usr.Identity.IsAuthenticated && usr.Identity.AuthenticationType == "Forms") {
FormsIdentity fIdent = usr.Identity as FormsIdentity;

// Create a CustomIdentity based on the FormsAuthenticationTicket CustomIdentity ci = new

CustomIdentity(fIdent.Ticket);

// Create the CustomPrincipal
CustomPrincipal p = new CustomPrincipal(ci);

// Attach the CustomPrincipal to HttpContext.User and Thread.CurrentPrincipal HttpContext.Current.User = p;


Thread.CurrentPrincipal = p;
}
}
</script>

該Application_OnPostAuthenticateRequest方法只在ASP.NET runtime引發(fā)PostAuthenticateRequest事件時(shí)才執(zhí)行,且在每個(gè)請求抵達(dá)時(shí)只發(fā)生一次.代碼首先檢查用戶是否通過了認(rèn)證,且認(rèn)證方式為表單認(rèn)證.如果是的,則創(chuàng)建一個(gè)新的CustomIdentity對象,并將當(dāng)前請求的票據(jù)傳入其構(gòu)造器.接下來,創(chuàng)建一個(gè)CustomPrincipal對象,并將剛才創(chuàng)建的CustomIdentity對象傳到其構(gòu)造器里.最后,將最新創(chuàng)建的CustomPrincipal對象賦值給當(dāng)前請求的安全內(nèi)容.

注意最后一步——將principal分配給HttpContext.User和Thread.CurrentPrincipal.這2步是必需的,因?yàn)槭茿SP.NET來處理的安全內(nèi)容.我們知道,.NET Framework將一個(gè)安全內(nèi)容與每個(gè)running thread聯(lián)系起來;我們可以通過Thread object的CurrentPrincipal屬性,以IPrincipal對象的形式來獲得這些信息. 容易讓人混淆的是ASP.NET也有自己的安全內(nèi)容信息(也就是HttpContext.User)

在某些情況下通過檢查Thread.CurrentPrincipal屬性來判定安全內(nèi)容;而在另一些情況下是通過檢查HttpContext.User屬性來實(shí)現(xiàn)的.比如,在.NET里有一些安全特性(security features),利用這些特性來聲明哪些用戶或角色可以使用一個(gè)類或調(diào)用某個(gè)特定的方法(請參閱《Adding Authorization Rules to Business and Data Layers Using PrincipalPermissionAttributes),此外,這些技術(shù)還可以通過Thread.CurrentPrincipal屬性來判定安全內(nèi)容.

我們再看看使用HttpContext.User用戶的情形.再前面的文章里,我們使用該屬性來顯示當(dāng)前登陸用戶的用戶名.自然的,Thread.CurrentPrincipal屬性和HttpContext.User屬性包含的安全內(nèi)容必須匹配.

ASP.NET runtime自動(dòng)的為我們同步的處理這些屬性.然而,該同步是發(fā)生在AuthenticateRequest事件之后,PostAuthenticateRequest事件之前。因此,當(dāng)在PostAuthenticateRequest事件里添加一個(gè)自定義principal的時(shí)候,我們需要手動(dòng)地為Thread.CurrentPrincipal賦值,不然Thread.CurrentPrincipal 和 HttpContext.User就不同步了. 更多細(xì)節(jié)請參閱《Context.User vs. Thread.CurrentPrincipal》.


訪問CompanyName 和 Title屬性

任何時(shí)候,當(dāng)請求抵達(dá)并由ASP.NET引擎分派時(shí),都會(huì)引發(fā)Global.asax文件里的Application_OnPostAuthenticateRequest事件,如果請求成功通過FormsAuthenticationModule的認(rèn)證的話,該事件處理器將創(chuàng)建一個(gè)新的CustomPrincipal對象,該對象將包含一個(gè)基于票據(jù)的CustomIdentity對象.在這里進(jìn)行這些邏輯處理后,我們就可以相當(dāng)方便地訪問當(dāng)前用戶的公司名和頭銜.

返回到Default.aspx頁面的Page_Load事件處理器,在第四步,我們在該事件處理器里寫代碼檢索票據(jù),并將UserData數(shù)據(jù)進(jìn)行解析以顯示公司名和頭銜.現(xiàn)在使用CustomPrincipal 和 CustomIdentity對象后,我們就不要對票據(jù)的UserData屬性進(jìn)行解析了. 僅僅需要獲取對CustomIdentity對象的引用,并使用其CompanyName和Title屬性,如下:

CustomIdentity ident = User.Identity as CustomIdentity;

if (ident != null) WelcomeBackMessage.Text += string.Format(" You are the {0} of {1}.", ident.Title, ident.CompanyName);

結(jié)語:

在本文,我們考察了如何通過Web.config文件來對表單認(rèn)證系統(tǒng)進(jìn)行配置.我們也考察了如何處理票據(jù)的有效期,以及encryption和validation安全措施是如何從inspection和modification這2方面對票據(jù)進(jìn)行保護(hù)的.最后,我們探討了使用票據(jù)的UserData屬性來存儲票據(jù)的附加信息.,以及如何使用自定義的principal和identity對象來以一種更好的方式訪問這些附加信息.

到本文為止,我們考察完了ASP.NET里的表單認(rèn)證.下一篇文章我們將開始考察Membership framework.

祝編程快樂!

Security Tutorials系列文章第三章:Forms Authentication Configuration and Advanced Topics


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會(huì)非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 云阳县| 鹤峰县| 漳浦县| 屯门区| 秦皇岛市| 历史| 靖远县| 宜黄县| 睢宁县| 忻州市| 富裕县| 奇台县| 衡阳市| 黄山市| 昌黎县| 牟定县| 九台市| 阿勒泰市| 蒙山县| 澜沧| 涟水县| 彝良县| 杂多县| 陆川县| 中西区| 澜沧| 化德县| 伊春市| 夏津县| 沂水县| 潮州市| 石渠县| 筠连县| 白玉县| 陆河县| 蚌埠市| 平乡县| 英吉沙县| 西乡县| 运城市| 普安县|