Asp.net單點登陸解決方案

原文出處:http://www.cnblogs.com/wu-jian算法

 

主站:Passport集中驗證服務器,DEMO中爲:http://www.passport.com/數據庫

分站:http://www.a.com/http://www.b.com/http://www.c.com/ 跨域

憑證:用戶登陸後產生的鑑權標識,用於識別受權用戶。可爲多種方式,DEMO中主站使用的是Cache,分站使用的是Session。瀏覽器

令牌:由Passport頒發可在各分站中流通的用戶惟一鑑權標識,DEMO中使用的是Cookie。緩存

 

核心邏輯安全

使用集中式身份驗證,用戶數據存放於Passport,各分站統一使用Passport進行登陸和鑑權,如<圖1>所示:服務器

<圖1>網絡

<圖2>數據結構

<圖2>詳細描述了單點登陸邏輯與流程ide

流程一:匿名用戶訪問分站a

匿名用戶訪問分站a上的一個受權頁面,首先跳轉到主站經過賬號、密碼進行登陸鑑權,驗證經過後產生主站憑證,同時產生令牌,跳轉回分站a。

此時分站a檢測到用戶已持有令牌,因而用令牌再次去主站獲取用戶憑證,獲取成功後容許用戶訪問該受權頁面。同時產生分站a的本地憑證。

當該用戶須要再次驗證時將使用本地憑證,以減小網絡交互。

流程二:在分站a登陸的用戶訪問分站b

由於用戶在分站a登陸過,已持有令牌,因此分站b會用令牌去主站獲取用戶憑證,獲取成功後容許用戶訪問受權頁面。同時產生分站b的本地憑證。

 

實現關鍵點

令牌

令牌由主站頒發,主站頒發令牌同時生成用戶憑證,並記錄令牌與用戶憑證之間的對應關係,以根據用戶提供的令牌響應對應的憑證;

令牌要在各跨域分站中進行流通,DEMO中使用了Cookie,並指定Cookie.Domain="passport.com";

各分站如何共享主站的Cookie?從分站Redirect到主站頁面,而後該頁面讀取Cookie並以URL參數方式回傳便可,可在DEMO代碼中查看詳細實現。

 
//產生令牌
HttpCookie tokenCookie = new HttpCookie("Passport.Token");
tokenCookie.Domain = "passport.com";
//可以使用自定義算法避免Cookie非法複製
//tokenCookie.Values.Add("Key", "加密算法");
tokenCookie.Values.Add("Value", tokenValue);
Response.AppendCookie(tokenCookie); 
 

在以前的文章中有讀者提到令牌(Cookie)被非法複製致使的安全問題,在此簡單說明一下。

首先默認設置了Cookie爲關閉瀏覽器即失效,也就是說用戶在成功鑑權後持續打開瀏覽器的前提下才會導至令牌泄漏。

而後在Passport令牌的設計中有「過時時間」一項,也能夠經過使用令牌的過時時間來確保令牌安全。

若是您以爲還不夠,其實只須要在令牌驗證時添加一些自定義邏輯,好比用時間、用戶特徵產生一個Hash值做爲令牌的安全KEY。

文章主要針對SSO的邏輯,一些細節上的代碼並未十全十美,讀者能夠根據實際需求進行完善。

 

主站憑證

主站憑證是一個關係表,包含了三個字段:令牌、用戶憑證、過時時間。

主站憑證有多種實現方式可供選擇,好比要求可靠的話可使用數據庫,要求性能的話可使用Cache,DEMO中我使用的是Cache。以下代碼所示:

 
/// <summary>
/// 初始化緩存數據結構
/// </summary>
/// <remarks>
/// ----------------------------------------------------
/// | token(令牌) | cert(用戶憑證) | timeout(過時時間) |
/// |--------------------------------------------------|
/// </remarks>
private static void cacheInit()
{
    if (HttpContext.Current.Cache["PASSPORT.TOKEN"] == null)
    {
        DataTable dt = new DataTable();

        dt.Columns.Add("token", Type.GetType("System.String"));
        dt.Columns["token"].Unique = true;

        dt.Columns.Add("cert", Type.GetType("System.Object"));
        dt.Columns["cert"].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("PASSPORT.TOKEN", dt, null, DateTime.MaxValue, TimeSpan.FromMinutes(double.Parse(System.Configuration.ConfigurationManager.AppSettings["Timeout"]) * 2));
    }
}
 

 

分站憑證

分站憑證主要用於減小重複驗證時網絡的交互,好比用戶已在分站a上登陸過,當他再次訪問分站a時,就沒必要使用令牌去主站驗證了,由於分站a已有該用戶的憑證。

分站憑證相對比較簡單,使用Session、Cookie都可。

 

分站SSO頁面基類

分站使用SSO的頁面會作一系列的邏輯判斷處理,如<圖2>所示。若是爲每一個頁面寫一遍這樣的邏輯會很是繁瑣。OK,那麼把這套邏輯封裝成一個基類,凡是要使用SSO的頁面繼承該基類便可。以下代碼所示:

 
public class AuthBase : System.Web.UI.Page
{
    protected override void OnLoad(EventArgs e)
    {
        if (Session["A.Cert"] != null)
        {
            //分站憑證存在
            Response.Write("恭喜,分站憑證存在,您被受權訪問該頁面!");
        }
        else
        {
            //令牌驗證結果返回
            if (Request.QueryString["token"] != null)
            {
                //持有令牌
                if (Request.QueryString["token"] != "$token$")
                {
                    string tokenValue = Request.QueryString["token"];
                    //調用WebService獲取主站憑證
                    //防止令牌僞造
                    //此處還可以使用公鑰私鑰的非對稱加密策略
                    SSO.SiteA.ServiceReference1.PassportServiceSoapClient passportService = new SSO.SiteA.ServiceReference1.PassportServiceSoapClient();
                    object cert = passportService.TokenGetCert(tokenValue);
                    if (cert != null)
                    {
                        //令牌正確
                        Session["A.Cert"] = cert;
                        Response.Write("恭喜,令牌存在,您被受權訪問該頁面!");
                    }
                    else
                    {
                        //令牌錯誤,去Passport登陸
                        Response.Redirect(SSO.Common.Tools.TokenReplace());
                    }
                }
                //未持有令牌,去Passport登陸
                else
                {
                    Response.Redirect(SSO.Common.Tools.TokenReplace());
                }
            }
            //未進行令牌驗證,去Passport驗證
            else
            {
                //當前url附加上token參數
                Response.Redirect(SSO.Common.Tools.TokenUrl());
            }
        }

        base.OnLoad(e);
    }

}//end class
 


用戶退出

<圖3>

用戶退出時分別清空主站令牌/憑證與全部分站憑證。DEMO中經過調用WebService清空該用戶的主站令牌/憑證,經過iframe清空各分站憑證,請參閱DEMO中的詳細代碼實現。

 

主站過時令牌/憑證清除

讀者可自行實現該邏輯,定時清除(DataTable)Cache[「PASSPORT.TOKEN」]中timeout字段超過當前時間的記錄。

 

DEMO

DEMO開發環境

.Net Framework 4.0

Visual Studio 2012

 

在IIS中配置站點

配置4個站點指向相應的目錄,並分別指定4個站點的主機頭: 

http://www.passport.com/ 

http://www.a.com/ 

http://www.b.com/ 

http://www.c.com/ 

 

修改hosts文件以將域名解析到本地站點

127.0.0.1          http://www.passport.com/

127.0.0.1          http://www.a.com/

127.0.0.1          http://www.b.com/

127.0.0.1          http://www.c.com/ 

 

DEMO下載

 http://files.cnblogs.com/files/tinya/wujian_sso.rar

相關文章
相關標籤/搜索