參考 :
http://hi.baidu.com/iykQQlpugocfnqe/item/e132329bdea22acbb6253105 ASP.NET中處理請求的流程圖
http://www.49028c.com/yao/archive/2006/06/24/434783.html
http://www.49028c.com/fish-li/archive/2012/04/15/2450571.html#_label3
這篇主要說說實現和邏輯流程.
所謂認證和授權是指對一個服務器資源的訪問限制管理。
比如有一些文件是不公開的,只有管理人員能訪問的到,這要求他們必須先"登入"。這就是認證
那么授權是在認證之后發生的事情,管理人員也有分等級,好比公司有些機密文件只有上層可以看。這就是授權.
認證和授權微軟已經為我們做了很多封裝,form 認證就是其中一種。
不過,這里我們先想想,在原始年代,我們要如何去實現呢?
我們都知道web所有資源都是通過http請求來訪問的。顯然第一步是攔截所有不公開的資源.
第二步就是檢查他們是否"登入".
所謂的"登入"其實就是通過一個 cookie 來完成的。
如果請求沒有附帶指定的 cookie 那么就表示沒有登入,就該阻止訪問 (并跳轉到登入界面).
在登入頁面確認用戶密碼后,給予cookie,就表示登入了啦 .
第三步
通過了認證,我們必須查看這個用戶的身份(或者說角色), 比如是經理,主管,還是普通員工。
進一步的驗證用戶是否有足夠的權限(授權)來訪問這個資源。
好了,其實不太難。大至少就是這樣了。
以上步驟涉及到2個重要的點 :
1. 如果攔截特定的資源請求 ?
2. cookie 的安全性
下面我來個一個微軟封裝好的例子, 一般上普通項目夠用了。
1.在web config 加上一個 authentication
<system.web> <authentication mode="Forms"> <forms loginUrl="~/login/Default.aspx" timeout="2880" defaultUrl="~/" /> </authentication></system.web>
這里用 mode 是 forms (我也只會這個)
loginUrl 是登入頁面的路徑 , timeout 是說cookie 的有效時間 , defaultUrl 我不清楚
2. 做一個登入頁面,這里只是隨便做。你明白就可以了
PRotected void Page_Load(object sender, EventArgs e){ }protected void Button1_Click(object sender, EventArgs e) //登入{ //set一個cookie , name and 是否要持久cookie,false的話會base on web config 的timeout FormsAuthentication.SetAuthCookie("keatkeat", false); }protected void Button2_Click(object sender, EventArgs e) //注銷{ FormsAuthentication.SignOut(); }
3. 設定哪些文件路徑需要攔截認證
<configuration> <location path="securityFolder"> <system.web> <authorization> <deny users="?"/> </authorization> </system.web> </location> </configuration>
path 指定路徑,其下的所有folders files 都被限制了.
authorization 內的元素 有多種配搭模式
<deny users="?"> 基本上由 3 的東東做出來,
1. deny | allow (禁止 或者 允許)
2.users | roles | verbs ( users 用戶 , roles 角色比較特別,后面我會教你如何設置一個或多個角色在一個user身上,verbs 就是http method ,GET POST 等)
3. ? | * ( ? 代表匿名 , * 代表所有的)
所以上面這一句的解釋是 -禁止匿名用戶- (沒登入就無法訪問)
任何訪問都是 users="*" , 登入后就不再是 users="?"
完成以上的步驟基本上就可以做到一個簡單的認證授權機制了(不需要分角色的話)
它驗證的次序是這樣的,如果pass了就不會繼續驗證了,所以一般上都是先寫,deny 才寫 allow
那么如果我們要高級一點的呢?
<location path="securityFolder"> <system.web> <authorization> <allow roles="Admin,Boss"/> <deny users="*"/> </authorization> </system.web> </location>
允許角色為Admin或者Boss , 禁止所有用戶
現在我們必須把用戶的角色添加進用戶里 (因為從上面開來,我們只給了個Name給用戶)
class AuthenticateHttpModule : IHttpModule{ public void Dispose() { } public void Init(Httpapplication context) { context.AuthenticateRequest += new EventHandler(AuthenticateRequest); } private void AuthenticateRequest(object sender, EventArgs e) { HttpApplication app = (HttpApplication)sender; HttpContext ctx = app.Context; //獲取本次Http請求的HttpContext對象 if (ctx.User != null) { if (ctx.Request.IsAuthenticated == true) //驗證過的一般用戶才能進行角色驗證 { string name = ctx.User.Identity.Name; FormsIdentity fi = (System.Web.Security.FormsIdentity)ctx.User.Identity; //FormsAuthenticationTicket ticket = fi.Ticket; //取得身份驗證票 //string userData = ticket.UserData;//從UserData中恢復role信息 string[] roles = "Admin,zz".Split(','); //將角色數據轉成字符串數組,得到相關的角色信息 ctx.User = new GenericPrincipal(fi, roles); //這樣當前用戶就擁有角色信息了 } } }}
這里我們要寫一個 HttpModule 來完成 (記得web config 也要添加哦)
我們用 new GenericPrincipal 來添加角色進用戶里,這樣就可以了。
注 : 我們這個模塊是跑在微軟后面的,所以我們完全不需要從cookie里面獲取任何東西,直接用 context.User 就好了。
以上大概就是全部的過程了。
這里給一個自定義的例子 :
public class AdministratorIdentity : IIdentity{ public string AuthenticationType { get; set; } public string Name { get; set; } public bool IsAuthenticated { get; set; }}public class Administrator : IPrincipal{ public IIdentity Identity { get; set; } public string name { get; set; } //可以任意定義屬性 public bool IsInRole(string role) { if (role == "Admin") //個種你想的到的驗證手法都可以 { return true; } return false; }}
if (ctx.Request.IsAuthenticated == true) //驗證過的一般用戶才能進行角色驗證 { string name = ctx.User.Identity.Name; string type = ctx.User.Identity.AuthenticationType; //自定義 ctx.User = new Administrator { name = "keatkeat", Identity = new AdministratorIdentity { AuthenticationType = ctx.User.Identity.AuthenticationType, Name = "z", IsAuthenticated = true } }; //原版添加 roles 的方式 //FormsIdentity fi = (System.Web.Security.FormsIdentity)ctx.User.Identity; ////FormsAuthenticationTicket ticket = fi.Ticket; //取得身份驗證票 ////string userData = ticket.UserData;//從UserData中恢復role信息 //string[] roles = "Admin".Split(','); //將角色數據轉成字符串數組,得到相關的角色信息 //ctx.User = new GenericPrincipal(fi, roles); //這樣當前用戶就擁有角色信息了}
這樣到哪里只要 Ctx.User as Administrator 就可以容易的使用啦 ^^
這里也提一提使用 cookie 加密的安全性問題
第一,如果有人可以從你的電腦上獲取到你的cookie , 那么他就等于擁有了你所有權限了。
第二,如果他沒有入侵你的電腦,他是否可以自己創建一個加密的cookie來模擬你呢?
答案是不行,因為創建cookie時,加密是配合服務器的私鑰的。(好像叫對稱加密)
所以呢,基本上算是安全的。 參考 :http://blog.csdn.net/fancyf/article/details/348202
要自定義服務器的私鑰的話可以這樣寫 :
<configuration> <system.web> <machineKey validationKey="xxxxxxxxxxxxxxxxxxxxxxxx" decryptionKey="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" validation="SHA1" decryption="AES" /> </system.web> </configuration>
好像也可以指定一個程序來輸出 ,
machineKey 可以通過這個網站創建http://www.a2zmenu.com/utility/Machine-Key-Generator.aspx#
下面我另外談談我的一些開發經驗。
現今我們做的大部分是單頁面應用,只有一個登入頁面和一個主頁面,其它的頁面都是虛擬的。
如果是自己做 url rewrite 的話,要注意的是,請在ResolveRequestCache(認證授權模塊之后) 時才做.
以上的部分,如果你想自己實現也是完全可以的,cookie 的加密可以用微軟的加密方法,你也可以繼承 IPrincipal 來實現 自己的 User
也可以注冊 HttpModule 攔截AuthorizeRequest 比對路徑,去sql 拿用戶職位等等來做授權驗證。
還有如果你用的是 WebAPI 的話,建議要把這2者分開。以上說的攔截是針對頁面資源的訪問。
WebAPI 內部也有攔截認證和授權的機制。所以針對 WebAPI 的資源還是用用 WebAPI 本身的機制來管理比較妥當.
WebAPI 是支持self host,但是如果我們是使用IIS又貪方便的話,我們也可以直接用上面的form認證。
所以的API請求依然會通過IIS的 pipe,到了API controller ,User 依然是我們的 context.User
如果遇到是 self host 的話,其實也可以用上面的概念來做。只不過不使用cookie 改成使用 http header 來替代。
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(2, loginName, DateTime.Now, DateTime.Now.AddDays(1), true, data);string cookieValue = FormsAuthentication.Encrypt(ticket);FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookieValue);
這個加密解密做好其實原理依舊是通的啦。
總結 :
簡單的說 認證與授權 ,不外乎就是 請求時附上身份,響應前驗證身份。
新聞熱點
疑難解答