基於.Net的單點登入(SSO)實現解決方案

來源:互聯網
上載者:User
前些天一位朋友要我幫忙做一單點登入,其實這個概念早已耳熟能詳,但實際應用很少,難得最近輕閑,於是決定通過本文來詳細描述一個SSO解決方案,希望對大家有所協助。SSO的解決方案很多,但搜尋結果令人大失所望,大部分是相互轉載,並且描述的也是走馬觀花。
閑話少敘,進入正題,我的想法是使用集中驗證方式,多個網站集中Passport驗證。 如所示:

為方便清晰描述,先定義幾個名詞,本文中出現之處均為如下含義。
主站:Passport集中驗證伺服器 http://www.passport.com/ 。
分站:http://www.a.com/、http://www.b.com/、http://www.c.com/
憑證:使用者登入後產生的資料標識,用於識別授權使用者,可為多種方式,DEMO中主站我使用的是Cache,分站使用Session。
令牌:由Passport頒發可在各分站中流通的唯一標識。
OK,現在描述一下單點登入的過程:
情形一、匿名使用者:匿名使用者存取分站a上的一個授權頁面,首先跳轉到主站讓使用者輸入帳號、密碼進行登入,驗證通過後產生主站憑證,同時產生令牌,跳回分站a,此時分站a檢測到使用者已持有令牌,於是用令牌再次去主站擷取使用者憑證,擷取成功後允許使用者訪問該授權頁面。同時產生分站a的本地憑證,當該使用者需要再次驗證時將先檢查本地憑證,以減少網路互動。
情形二、在分站a登入的使用者訪問分站b:因為使用者在分站a登入過,已持有令牌,所以分站b會用令牌去主站擷取使用者憑證,擷取成功後允許使用者訪問授權頁面。同時產生分站b的本地憑證。

設計完成後,接下來是方案實現的一些關鍵點:
令牌:令牌由主站頒發,主站頒發令牌同時產生使用者憑證,並記錄令牌與使用者憑證之間的對應關係,以根據使用者提供的令牌響應對應的憑證;令牌要在各跨域分站中進行流通,所以DEMO中令牌我使用主站的Cookie,並指定Cookie.Domain="passport.com"。各分站如何共用主站的Cookie?從分站Redirect到主站頁面,然後該頁面讀取Cookie並以URL參數方式回傳即可,可在DEMO代碼中查看詳細實現,當然如果哪位有更好的令牌實現方式也拿出來分享。

//產生令牌 string tokenValue = Guid.NewGuid().ToString().ToUpper(); HttpCookie tokenCookie = new HttpCookie("Token"); tokenCookie.Values.Add("Value", tokenValue); tokenCookie.Domain = "passport.com"; Response.AppendCookie(tokenCookie);

主站憑證:主站憑證是一個關係表,包含了三個欄位:令牌、憑證資料、到期時間。有多種實現方式可供選擇,要求可靠的話用資料庫,要求效能的話用Cache,DEMO中我使用的是Cache中的DataTable。如下代碼所示:

/// <summary> /// 初始化資料結構 /// </summary> /// <remarks> /// ---------------------------------------------------- /// | token(令牌) | info(使用者憑證) | timeout(到期時間) | /// |--------------------------------------------------| /// </remarks> private static void cacheInit() { if (HttpContext.Current.Cache["CERT"] == null) { DataTable dt = new DataTable(); dt.Columns.Add("token", Type.GetType("System.String")); dt.Columns["token"].Unique = true; dt.Columns.Add("info", Type.GetType("System.Object")); dt.Columns["info"].DefaultValue = null; dt.Columns.Add("timeout", Type.GetType("System.DateTime")); dt.Columns["timeout"].DefaultValue = DateTime.Now.AddMinutes(double.Parse(System.Configuration.ConfigurationManager.AppSettings["timeout"])); DataColumn[] keys = new DataColumn[1]; keys[0] = dt.Columns["token"]; dt.PrimaryKey = keys; //Cache的到期時間為 令牌到期時間*2 HttpContext.Current.Cache.Insert("CERT", dt, null, DateTime.MaxValue, TimeSpan.FromMinutes(double.Parse(System.Configuration.ConfigurationManager.AppSettings["timeout"]) * 2)); } }

分站憑證:分站憑證主要用於減少重複驗證時網路的互動,比如使用者已在分站a上登入過,當他再次訪問分站a時,就不必使用令牌去主站驗證了,因為分站a已有該使用者的憑證。分站憑證相對比較簡單,使用Session、Cookie均可。
分站SSO頁面基類:分站使用SSO的頁面會做一系列的邏輯判斷處理,如文章開頭的流程圖。如果有多個頁面的話不可能為每個頁寫一個這樣的邏輯,OK,那麼把這套邏輯封裝成一個基類,凡是要使用SSO的頁面繼承該基類即可。如下代碼所示:

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; using System.Text.RegularExpressions; namespace SSO.SiteA.Class { /// <summary> /// 授權頁面基類 /// </summary> public class AuthBase : System.Web.UI.Page { protected override void OnLoad(EventArgs e) { if (Session["Token"] != null) { //分站憑證存在 Response.Write("恭喜,分站憑證存在,您被授權訪問該頁面!"); } else { //令牌驗證結果 if (Request.QueryString["Token"] != null) { if (Request.QueryString["Token"] != "$Token$") { //持有令牌 string tokenValue = Request.QueryString["Token"]; //調用WebService擷取主站憑證 SSO.SiteA.RefPassport.TokenService tokenService = new SSO.SiteA.RefPassport.TokenService(); object o = tokenService.TokenGetCredence(tokenValue); if (o != null) { //令牌正確 Session["Token"] = o; Response.Write("恭喜,令牌存在,您被授權訪問該頁面!"); } else { //令牌錯誤 Response.Redirect(this.replaceToken()); } } else { //未持有令牌 Response.Redirect(this.replaceToken()); } } //未進行令牌驗證,去主站驗證 else { Response.Redirect(this.getTokenURL()); } } base.OnLoad(e); } /// <summary> /// 擷取帶令牌請求的URL /// 在當前URL中附加上令牌請求參數 /// </summary> /// <returns></returns> private string getTokenURL() { string url = Request.Url.AbsoluteUri; Regex reg = new Regex(@"^.*\?.+=.+$"); if (reg.IsMatch(url)) url += "&Token=$Token$"; else url += "?Token=$Token$"; return "http://www.passport.com/gettoken.aspx?BackURL=" + Server.UrlEncode(url); } /// <summary> /// 去掉URL中的令牌 /// 在當前URL中去掉令牌參數 /// </summary> /// <returns></returns> private string replaceToken() { string url = Request.Url.AbsoluteUri; url = Regex.Replace(url, @"(\?|&)Token=.*", "", RegexOptions.IgnoreCase); return "http://www.passport.com/userlogin.aspx?BackURL=" + Server.UrlEncode(url); } }//end class }

使用者退出:使用者退出時分別清空主站憑證與當前分站憑證。如果要求A網站退出,B、C網站也退出,可自行擴充介面清空每個分站憑證。
主站到期憑證/令牌清除:定時清除(DataTable)Cache[“CERT”]中timeout欄位超過目前時間的記錄。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.