這篇文章介紹了
NET單點登陸的實現方法及思路
有需要的朋友可以參考一下
希望對你有所幫助
系統的基本架構
我們假設一個系統System包含Service客戶服務中心Shop網上購物中心和Office網上辦公中心三個獨立的網站 Service管理客戶的資料登錄和注銷過程不論客戶訪問System的任何一個頁面系統都會轉到登錄界面在用戶登錄後系統會自動轉會到客戶上 次請求的頁面並且用戶此後可以在System中無縫切換不需要再次進行登錄即在System中實現單點登錄SSO(Single SignOn)
我們知道用戶的即時狀態通常是使用ApplicationSessionCookie和存儲的而這些都是不能在程序中跨站點訪問的我們必需通過站點間相互通訊來確認用戶的即時狀態
簡單的實現
第一步假設用戶訪問了Shop或Office的任何一個頁面Any該頁面所在的網站將會檢查用戶的即時狀態如果用戶已經登錄了則將 Any頁面的信息返回給用戶如果用戶還沒有登錄則自動轉到Service的Validate頁面驗證用戶在Service狀態即Shop或 Office向Service發出請求要求Service返回用戶的即時狀態
第二步Validate驗證用戶的即時狀態如果 用戶已經登錄了則Service將用戶的即時狀態返回給Shop或Office的同步頁面 Synchronous通知Shop或Office同步用戶狀態如果用戶沒有登錄則自動轉向Customer頁面提示用戶登錄
第三步用戶完成登錄過程當用戶成功登錄後自動轉回Validate頁面通知Shop或Office的Synchronous進行用戶狀態的同步
第四步在用戶狀態同步完成後在本地站點用戶狀態成為在線狀態即可訪問Any頁面
在上面的流程中我們知道不管用戶訪問哪個站點用戶只需要一次登錄就保證用戶在Service的即時狀態都是在線的不會再需要進行第二次登錄的過程
現在我們的思路已經清楚具體的實現我們將在代碼分析中完成
代碼分析
從上面的流程中我們可以看出系統中Shop和Office的代碼是完全類似的只要Shop可以實現Office也可以同樣的克隆所以我們的重點分析的對象是Shop和Service的代碼
Shop的Webconfig和Projectcs
在Shop的Webconfig裡我們配置了Service站點和Shop站點以方便我們在部署時方便修改
復制代碼 代碼如下:
<appsettings>
<add key="Service" value="http://localhost:
" />
<add key="WebSite" value="http://localhost:
" />
</appsettings>
在Project類裡進行引用
復制代碼 代碼如下:
using System;
using System
Configuration;
namespace Amethysture
SSO
Shop
{
public class Project
{
public static string Service = ConfigurationSettings
AppSettings["Service"];
public static string WebSite = ConfigurationSettings
AppSettings["WebSite"];
}
}
Shop的Globalcs
Shop的Globalcs定義了四個Session變量UserID用來標識用 戶身份Pass標識用戶即時狀態Security用於保存往來Service和Shop的通訊不是被仿冒的Url保存了上次請求的頁面以保證在用 戶登錄後能轉到用戶請求的頁面
復制代碼 代碼如下:
protected void Session_Start(Object sender
EventArgs e)
{
this
Session
Add("UserID"
);
this
Session
Add("Pass"
false);
this
Session
Add("Security"
"");
this
Session
Add("Url"
"");
}
Shop的Anycs
Shop的Anycs並沒有包含代碼因為Any類從Page繼承而來為了代碼分析方便我們將代碼集中到Pagecs中
復制代碼 代碼如下:
using System;
using System
Web;
namespace Amethysture
SSO
Shop
{
public class Any : Amethysture
SSO
Shop
Page
{
}
}
Shop的Pagecs
Page類有兩個方法CustomerValidate和InitializeCustomerValidate用戶檢查用戶的即時狀態而Initialize是頁面登錄後發送給用戶的信息我們的重點是CustomerValidate
CustomerValidate是一個非常簡單的流程用條件語句檢查Pass的狀態如果Pass為否則表示用戶沒有登錄頁面跳轉到 Service的Validate頁面中我們要分析的是其中保存的Url和遞交的WebSite和Security幾個參數Url的作用在前面已經講 清楚了只是為了保證用戶登錄後能回到原來的頁面而WebSite是為了保證該站點是被Service所接受的並且保證Service知道是哪個站點 請求了用戶即時狀態因為這個例子是個簡單的例子在後面的Validate裡沒有驗證WebSite是否是接受的請求站點但是在實際應用中應該驗證這 一點因為Shop和Service等同於服務器和客戶端服務器出於安全考慮必須要檢查客戶端是否是被允許的Security是非常重要的一點 Shop對Service發送的是請求不需要保證該請求沒有被篡改但是在Service應答Shop請求時就必須要保證應答的數據沒有被篡改了 Security正是為了保證數據安全而設計的
在代碼中Security是通過Hash一個隨機產生的數字生成的具有不確定 性和保密性我們可以看到Security同時保存在Session中和發送給Service我們把這個Security當作明文在後面我們可以 看到Security在Service經過再一次Hash後作為密文發送回Shop如果我們將Session保存的Security經過同樣的 Hash方法處理後等到的字符串如果和Service返回的密文相同我們就能夠在一定程度上保證Service應答的數據是沒有經過修改的
復制代碼 代碼如下:
using System;
using System
Web;
using System
Security
Cryptography;
using System
Text;
namespace Amethysture
SSO
Shop
{
public class Page : System
Web
UI
Page
{
private void CustomerValidate()
{
bool Pass = (bool) this
Session["Pass"];
if (!Pass)
{
string Security = "";
Random Seed = new Random();
Security = Seed
Next(
int
MaxValue)
ToString();
byte[] Value;
UnicodeEncoding Code = new UnicodeEncoding();
byte[] Message = Code
GetBytes(Security);
SHA
Managed Arithmetic = new SHA
Managed();
Value = Arithmetic
ComputeHash(Message);
Security = "";
foreach(byte o in Value)
{
Security += (int) o + "O";
}
this
Session["Security"] = Security;
this
Session["Url"] = this
Request
RawUrl;
this
Response
Redirect(Project
Service + "/Validate
aspx?WebSite=" + Project
WebSite + "&Security=" + Security);
}
}
protected virtual void Initialize()
{
this
Response
Write("<html>");
this
Response
Write("<head>");
this
Response
Write("<title>Amethysture SSO Project</title>");
this
Response
Write("<link rel=stylesheet type="text/css" href="" + project
website + "/Default
css">");
this
Response
Write("</head>");
this
Response
Write("<body>");
this
Response
Write("<iframe width="
" height="
" src="" + project
service + "/Customer
aspx"></iframe>");
this
Response
Write("<div align="center">");
this
Response
Write("Amethysture SSO Shop Any Page");
this
Response
Write("</div>");
this
Response
Write("</body>");
this
Response
Write("</html>");
}
protected override void OnInit(EventArgs e)
{
base
OnInit(e);
this
CustomerValidate();
this
Initialize();
this
Response
End();
}
}
}
Service的Globalcs
現在我們頁面轉到了Service的Validate頁面我們轉過來看 Service的代碼在Global中我們同樣定義了四個Session變量都和Shop的Session用處類似WebSite是保存請求用戶即 時狀態的站點信息以便能在登錄後返回正確的請求站點
復制代碼 代碼如下:
protected void Session_Start(Object sender
EventArgs e)
{
this
Session
Add("UserID"
);
this
Session
Add("Pass"
false);
this
Session
Add("WebSite"
"");
this
Session
Add("Security"
"");
}
Service的Validatecs
首先將Shop傳遞過來的參數保存到Session中如果用戶沒有登錄則轉到Customer頁面進行登錄如果用戶已經登錄了則將用戶即時狀態傳回給Shop站點如上所述這裡將Security重新Hash了一次傳回給Shop以保證數據不被纂改
復制代碼 代碼如下:
private void CustomerValidate()
{
bool Pass = (bool) this
Session["Pass"];
if ((this
Request
QueryString["WebSite"] != null) && (this
Request
QueryString["WebSite"] != ""))
{
this
Session["WebSite"] = this
Request
QueryString["WebSite"];
}
if ((this
Request
QueryString["Security"] != null) && (this
Request
QueryString["Security"] != ""))
{
this
Session["Security"] = this
Request
QueryString["Security"];
}
if (Pass)
{
string UserID = this
Session["UserID"]
ToString();
string WebSite = this
Session["WebSite"]
ToString();
string Security = this
Session["Security"]
ToString();
byte[] Value;
UnicodeEncoding Code = new UnicodeEncoding();
byte[] Message = Code
GetBytes(Security);
SHA
Managed Arithmetic = new SHA
Managed();
Value = Arithmetic
ComputeHash(Message);
Security = "";
foreach(byte o in Value)
{
Security += (int) o + "O";
}
this
Response
Redirect(WebSite + "/Synchronous
aspx?UserID=" + UserID + "&Pass=True&Security=" + Security);
}
else
{
this
Response
Redirect("Customer
aspx");
}
}
Service的Customercs和Logincs
Customer主要的是一個用於登錄的表單這裡就不 貼出代碼了這裡分析一下Login的一段代碼這段代碼是當登錄是直接在Service完成的(WebSite為空值)則頁面不會轉到Shop或 Office站點所以應該暫停在Service站點系統如果比較完美這裡應該顯示一組字系統的轉向鏈接下面我們看到當Pass為真時頁面轉回 到Validate頁面通過上面的分析我們知道頁面會轉向Shop的Synchronous頁面進行用戶狀態的同步
復制代碼 代碼如下:
if (Pass)
{
if ((this
Session["WebSite"]
ToString() != "") && (this
Session["Security"]
ToString() != ""))
{
this
Response
Redirect("Validate
aspx");
}
else
{
this
Response
Write("");
this
Response
Write("");
this
Response
Write("");
this
Response
Write("");
this
Response
Write("");
this
Response
Write("");
this
Response
Write("");
this
Response
Write("Pass");
this
Response
Write("");
this
Response
Write("");
this
Response
Write("");
}
}
else
{
this
Response
Redirect("Customer
aspx");
}
Shop的Synchronouscs
好了我們在Service中完成了登錄並把用戶狀態傳遞回Shop站 點我們接著看用戶狀態是怎麼同步的首先如果Session裡的Security是空字符串則表示Shop站點沒有向Service發送過請求而 Service向Shop發回了請求這顯然是錯誤的這次訪問是由客戶端偽造進行的訪問於是訪問被拒絕了同樣Security和 InSecurity不相同則表示請求和應答是不匹配的可能應答被纂改過了所以應答同樣被拒絕了當檢驗Security通過後我們保證 Serive完成了應答並且返回了確切的參數下面就是讀出參數同步Shop站點和Service站點的用戶即時狀態
復制代碼 代碼如下:
string InUserID = this
Request
QueryString["UserID"];
string InPass = this
Request
QueryString["Pass"];
string InSecurity = this
Request
QueryString["Security"];
string Security = this
Session["Security"]
ToString();
if (Security != "")
{
byte[] Value;
UnicodeEncoding Code = new UnicodeEncoding();
byte[] Message = Code
GetBytes(Security);
SHA
Managed Arithmetic = new SHA
Managed();
Value = Arithmetic
ComputeHash(Message);
Security = "";
foreach(byte o in Value)
{
Security += (int) o + "O";
}
if (Security == InSecurity)
{
if (InPass == "True")
{
this
Session["UserID"] = int
Parse(InUserID);
this
Session["Pass"] = true;
this
Response
Redirect(this
Session["Url"]
ToString());
}
}
else
{
this
Response
Write("");
this
Response
Write("");
this
Response
Write("");
this
Response
Write("");
this
Response
Write("");
this
Response
Write("");
this
Response
Write("");
this
Response
Write("數據錯誤");
this
Response
Write("");
this
Response
Write("");
this
Response
Write("");
}
}
else
{
this
Response
Write("");
this
Response
Write("");
this
Response
Write("");
this
Response
Write("");
this
Response
Write("");
this
Response
Write("");
this
Response
Write("");
this
Response
Write("訪問錯誤");
this
Response
Write("");
this
Response
Write("");
this
Response
Write("");
}
Shop的Pagecs
我們知道頁面在一段時間不刷新後Session會超時失效在我們一直訪問Shop的 時候怎麼才能保證Service的Session不會失效呢?很簡單我們返回來看Shop的Pagecs通過在所有Shop的頁面內都用 <iframe>嵌套Service的某個頁面就能保證Service能和Shop的頁面同時刷新需要注意的一點是Service的Session必 須保證不小於所有Shop和Office的Session超時時間這個在Webconfig裡可以進行配置
復制代碼 代碼如下:
this
Response
Write("<iframe width="
" height="
" src="" + project
service + "/Customer
aspx"></iframe>");
總結
一次完整的登錄完成了我們接著假設一下現在要跳到Office的Any頁面系統會進行怎樣的操作 呢?Any(用戶沒有登錄)>Validate(用戶已經登錄)>Synchronous(同步)>Any也就是說這次用戶沒有進行登錄的過 程我們通過一次登錄使得Service的用戶狀態為登錄並且不管有多少個網站應用只要這些網站都保證符合Shop的特性這些網站就都能保持 Service的用戶狀態同時能通過Service獲得用戶的狀態也就是說我們實現了SSO
From:http://tw.wingwit.com/Article/program/net/201311/14479.html