.NET單點(diǎn)登陸的實(shí)現(xiàn)方法及思路
更新時(shí)間:2013年07月26日 11:13:01 作者:
這篇文章介紹了.NET單點(diǎn)登陸的實(shí)現(xiàn)方法及思路,有需要的朋友可以參考一下,希望對(duì)你有所幫助
系統(tǒng)的基本架構(gòu)
我們假設(shè)一個(gè)系統(tǒng)System包含Service客戶(hù)服務(wù)中心、Shop網(wǎng)上購(gòu)物中心和Office網(wǎng)上辦公中心三個(gè)獨(dú)立的網(wǎng)站。 Service管理客戶(hù)的資料,登錄和注銷(xiāo)過(guò)程。不論客戶(hù)訪(fǎng)問(wèn)System的任何一個(gè)頁(yè)面,系統(tǒng)都會(huì)轉(zhuǎn)到登錄界面,在用戶(hù)登錄后,系統(tǒng)會(huì)自動(dòng)轉(zhuǎn)會(huì)到客戶(hù)上 次請(qǐng)求的頁(yè)面。并且用戶(hù)此后可以在System中無(wú)縫切換。不需要再次進(jìn)行登錄。即在System中實(shí)現(xiàn)單點(diǎn)登錄SSO(Single Sign-On)。
我們知道,用戶(hù)的即時(shí)狀態(tài)通常是使用Application、Session、Cookie和存儲(chǔ)的。而這些都是不能在程序中跨站點(diǎn)訪(fǎng)問(wèn)的。我們必需通過(guò)站點(diǎn)間相互通訊來(lái)確認(rèn)用戶(hù)的即時(shí)狀態(tài)。
簡(jiǎn)單的實(shí)現(xiàn)
第一步,假設(shè)用戶(hù)訪(fǎng)問(wèn)了Shop或Office的任何一個(gè)頁(yè)面Any。該頁(yè)面所在的網(wǎng)站將會(huì)檢查用戶(hù)的即時(shí)狀態(tài)。如果用戶(hù)已經(jīng)登錄了,則將 Any頁(yè)面的信息返回給用戶(hù)。如果用戶(hù)還沒(méi)有登錄,則自動(dòng)轉(zhuǎn)到Service的Validate頁(yè)面,驗(yàn)證用戶(hù)在Service狀態(tài)。即Shop或 Office向Service發(fā)出請(qǐng)求,要求Service返回用戶(hù)的即時(shí)狀態(tài)。
第二步,Validate驗(yàn)證用戶(hù)的即時(shí)狀態(tài),如果 用戶(hù)已經(jīng)登錄了,則Service將用戶(hù)的即時(shí)狀態(tài)返回給Shop或Office的同步頁(yè)面 Synchronous,通知Shop或Office同步用戶(hù)狀態(tài)。如果用戶(hù)沒(méi)有登錄,則自動(dòng)轉(zhuǎn)向Customer頁(yè)面,提示用戶(hù)登錄。
第三步,用戶(hù)完成登錄過(guò)程,當(dāng)用戶(hù)成功登錄后,自動(dòng)轉(zhuǎn)回Validate頁(yè)面,通知Shop或Office的Synchronous進(jìn)行用戶(hù)狀態(tài)的同步。
第四步,在用戶(hù)狀態(tài)同步完成后,在本地站點(diǎn),用戶(hù)狀態(tài)成為在線(xiàn)狀態(tài),即可訪(fǎng)問(wèn)Any頁(yè)面。
在上面的流程中。我們知道,不管用戶(hù)訪(fǎng)問(wèn)哪個(gè)站點(diǎn),用戶(hù)只需要一次登錄,就保證用戶(hù)在Service的即時(shí)狀態(tài)都是在線(xiàn)的,不會(huì)再需要進(jìn)行第二次登錄的過(guò)程。
現(xiàn)在我們的思路已經(jīng)清楚,具體的實(shí)現(xiàn)我們將在代碼分析中完成。
代碼分析
從上面的流程中我們可以看出,系統(tǒng)中Shop和Office的代碼是完全類(lèi)似的。只要Shop可以實(shí)現(xiàn),Office也可以同樣的克隆。所以我們的重點(diǎn)分析的對(duì)象是Shop和Service的代碼。
1、Shop的Web.config和Project.cs
在Shop的Web.config里,我們配置了Service站點(diǎn)和Shop站點(diǎn),以方便我們?cè)诓渴饡r(shí)方便修改。
<appsettings>
<add key="Service" value="http://localhost:8001" />
<add key="WebSite" value="http://localhost:8002" />
</appsettings>
在Project類(lèi)里進(jìn)行引用。
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"];
}
}
2、Shop的Global.cs
Shop的Global.cs定義了四個(gè)Session變量,UserID用來(lái)標(biāo)識(shí)用 戶(hù)身份。Pass標(biāo)識(shí)用戶(hù)即時(shí)狀態(tài),Security用于保存往來(lái)Service和Shop的通訊不是被仿冒的。Url保存了上次請(qǐng)求的頁(yè)面,以保證在用 戶(hù)登錄后能轉(zhuǎn)到用戶(hù)請(qǐng)求的頁(yè)面。
protected void Session_Start(Object sender, EventArgs e)
{
this.Session.Add("UserID", 0);
this.Session.Add("Pass", false);
this.Session.Add("Security", "");
this.Session.Add("Url", "");
}
3、Shop的Any.cs
Shop的Any.cs并沒(méi)有包含代碼,因?yàn)锳ny類(lèi)從Page繼承而來(lái),為了代碼分析方便,我們將代碼集中到Page.cs中。
using System;
using System.Web;
namespace Amethysture.SSO.Shop
{
public class Any : Amethysture.SSO.Shop.Page
{
}
}
4、Shop的Page.cs
Page類(lèi)有兩個(gè)方法,CustomerValidate和Initialize。CustomerValidate用戶(hù)檢查用戶(hù)的即時(shí)狀態(tài),而Initialize是頁(yè)面登錄后發(fā)送給用戶(hù)的信息。我們的重點(diǎn)是CustomerValidate。
CustomerValidate是一個(gè)非常簡(jiǎn)單的流程,用條件語(yǔ)句檢查Pass的狀態(tài),如果Pass為否,則表示用戶(hù)沒(méi)有登錄,頁(yè)面跳轉(zhuǎn)到 Service的Validate頁(yè)面中。我們要分析的是其中保存的Url和遞交的WebSite和Security幾個(gè)參數(shù)。Url的作用在前面已經(jīng)講 清楚了,只是為了保證用戶(hù)登錄后能回到原來(lái)的頁(yè)面。而WebSite是為了保證該站點(diǎn)是被Service所接受的,并且保證Service知道是哪個(gè)站點(diǎn) 請(qǐng)求了用戶(hù)即時(shí)狀態(tài)。因?yàn)檫@個(gè)例子是個(gè)簡(jiǎn)單的例子,在后面的Validate里沒(méi)有驗(yàn)證WebSite是否是接受的請(qǐng)求站點(diǎn),但是在實(shí)際應(yīng)用中應(yīng)該驗(yàn)證這 一點(diǎn),因?yàn)镾hop和Service等同于服務(wù)器和客戶(hù)端,服務(wù)器出于安全考慮必須要檢查客戶(hù)端是否是被允許的。Security是非常重要的一點(diǎn)。 Shop對(duì)Service發(fā)送的是請(qǐng)求,不需要保證該請(qǐng)求沒(méi)有被篡改,但是在Service應(yīng)答Shop請(qǐng)求時(shí)就必須要保證應(yīng)答的數(shù)據(jù)沒(méi)有被篡改了。 Security正是為了保證數(shù)據(jù)安全而設(shè)計(jì)的。
在代碼中,Security是通過(guò)Hash一個(gè)隨機(jī)產(chǎn)生的數(shù)字生成的。具有不確定 性。和保密性。我們可以看到,Security同時(shí)保存在Session中和發(fā)送給Service。我們把這個(gè)Security當(dāng)作明文。在后面我們可以 看到,Security在Service經(jīng)過(guò)再一次Hash后作為密文發(fā)送回Shop。如果我們將Session保存的Security經(jīng)過(guò)同樣的 Hash方法處理后等到的字符串如果和Service返回的密文相同,我們就能夠在一定程度上保證Service應(yīng)答的數(shù)據(jù)是沒(méi)有經(jīng)過(guò)修改的。
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(1, int.MaxValue).ToString();
byte[] Value;
UnicodeEncoding Code = new UnicodeEncoding();
byte[] Message = Code.GetBytes(Security);
SHA512Managed Arithmetic = new SHA512Managed();
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="0" height="0" 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();
}
}
}
5、Service的Global.cs
現(xiàn)在我們頁(yè)面轉(zhuǎn)到了Service的Validate頁(yè)面,我們轉(zhuǎn)過(guò)來(lái)看 Service的代碼。在Global中我們同樣定義了四個(gè)Session變量,都和Shop的Session用處類(lèi)似。WebSite是保存請(qǐng)求用戶(hù)即 時(shí)狀態(tài)的站點(diǎn)信息。以便能在登錄后返回正確的請(qǐng)求站點(diǎn)。
protected void Session_Start(Object sender, EventArgs e)
{
this.Session.Add("UserID", 0);
this.Session.Add("Pass", false);
this.Session.Add("WebSite", "");
this.Session.Add("Security", "");
}
6、Service的Validate.cs
首先,將Shop傳遞過(guò)來(lái)的參數(shù)保存到Session中。如果用戶(hù)沒(méi)有登錄,則轉(zhuǎn)到Customer頁(yè)面進(jìn)行登錄。如果用戶(hù)已經(jīng)登錄了。則將用戶(hù)即時(shí)狀態(tài)傳回給Shop站點(diǎn)。如上所述,這里將Security重新Hash了一次傳回給Shop,以保證數(shù)據(jù)不被纂改。
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);
SHA512Managed Arithmetic = new SHA512Managed();
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");
}
}
7、Service的Customer.cs和Login.cs
Customer主要的是一個(gè)用于登錄的表單,這里就不 貼出代碼了。這里分析一下Login的一段代碼,這段代碼是當(dāng)?shù)卿浭侵苯釉赟ervice完成的(WebSite為空值),則頁(yè)面不會(huì)轉(zhuǎn)到Shop或 Office站點(diǎn)。所以應(yīng)該暫停在Service站點(diǎn)。系統(tǒng)如果比較完美,這里應(yīng)該顯示一組字系統(tǒng)的轉(zhuǎn)向鏈接。下面我們看到,當(dāng)Pass為真時(shí),頁(yè)面轉(zhuǎn)回 到Validate頁(yè)面,通過(guò)上面的分析,我們知道,頁(yè)面會(huì)轉(zhuǎn)向Shop的Synchronous頁(yè)面,進(jìn)行用戶(hù)狀態(tài)的同步。
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");
}
8、Shop的Synchronous.cs
好了,我們?cè)赟ervice中完成了登錄,并把用戶(hù)狀態(tài)傳遞回Shop站 點(diǎn)。我們接著看用戶(hù)狀態(tài)是怎么同步的。首先,如果Session里的Security是空字符串,則表示Shop站點(diǎn)沒(méi)有向Service發(fā)送過(guò)請(qǐng)求,而 Service向Shop發(fā)回了請(qǐng)求,這顯然是錯(cuò)誤的。這次訪(fǎng)問(wèn)是由客戶(hù)端偽造進(jìn)行的訪(fǎng)問(wèn),于是訪(fǎng)問(wèn)被拒絕了。同樣Security和 InSecurity不相同,則表示請(qǐng)求和應(yīng)答是不匹配的??赡軕?yīng)答被纂改過(guò)了,所以應(yīng)答同樣被拒絕了。當(dāng)檢驗(yàn)Security通過(guò)后,我們保證 Serive完成了應(yīng)答,并且返回了確切的參數(shù),下面就是讀出參數(shù)同步Shop站點(diǎn)和Service站點(diǎn)的用戶(hù)即時(shí)狀態(tài)。
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);
SHA512Managed Arithmetic = new SHA512Managed();
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("數(shù)據(jù)錯(cuò)誤");
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("訪(fǎng)問(wèn)錯(cuò)誤");
this.Response.Write("");
this.Response.Write("");
this.Response.Write("");
}
9、Shop的Page.cs
我們知道,頁(yè)面在一段時(shí)間不刷新后,Session會(huì)超時(shí)失效,在我們一直訪(fǎng)問(wèn)Shop的 時(shí)候怎么才能保證Service的Session不會(huì)失效呢?很簡(jiǎn)單,我們返回來(lái)看Shop的Page.cs。通過(guò)在所有Shop的頁(yè)面內(nèi)都用 <iframe>嵌套Service的某個(gè)頁(yè)面,就能保證Service能和Shop的頁(yè)面同時(shí)刷新。需要注意的一點(diǎn)是Service的Session必 須保證不小于所有Shop和Office的Session超時(shí)時(shí)間。這個(gè)在Web.config里可以進(jìn)行配置。
this.Response.Write("<iframe width="0" height="0" src="" + project.service + "/Customer.aspx"></iframe>");
總結(jié)
一次完整的登錄完成了。我們接著假設(shè)一下現(xiàn)在要跳到Office的Any頁(yè)面,系統(tǒng)會(huì)進(jìn)行怎樣的操作 呢?Any(用戶(hù)沒(méi)有登錄)->Validate(用戶(hù)已經(jīng)登錄)->Synchronous(同步)->Any。也就是說(shuō)這次,用戶(hù)沒(méi)有進(jìn)行登錄的過(guò) 程。我們通過(guò)一次登錄,使得Service的用戶(hù)狀態(tài)為登錄,并且不管有多少個(gè)網(wǎng)站應(yīng)用,只要這些網(wǎng)站都保證符合Shop的特性,這些網(wǎng)站就都能保持 Service的用戶(hù)狀態(tài),同時(shí)能通過(guò)Service獲得用戶(hù)的狀態(tài)。也就是說(shuō)我們實(shí)現(xiàn)了SSO
我們假設(shè)一個(gè)系統(tǒng)System包含Service客戶(hù)服務(wù)中心、Shop網(wǎng)上購(gòu)物中心和Office網(wǎng)上辦公中心三個(gè)獨(dú)立的網(wǎng)站。 Service管理客戶(hù)的資料,登錄和注銷(xiāo)過(guò)程。不論客戶(hù)訪(fǎng)問(wèn)System的任何一個(gè)頁(yè)面,系統(tǒng)都會(huì)轉(zhuǎn)到登錄界面,在用戶(hù)登錄后,系統(tǒng)會(huì)自動(dòng)轉(zhuǎn)會(huì)到客戶(hù)上 次請(qǐng)求的頁(yè)面。并且用戶(hù)此后可以在System中無(wú)縫切換。不需要再次進(jìn)行登錄。即在System中實(shí)現(xiàn)單點(diǎn)登錄SSO(Single Sign-On)。
我們知道,用戶(hù)的即時(shí)狀態(tài)通常是使用Application、Session、Cookie和存儲(chǔ)的。而這些都是不能在程序中跨站點(diǎn)訪(fǎng)問(wèn)的。我們必需通過(guò)站點(diǎn)間相互通訊來(lái)確認(rèn)用戶(hù)的即時(shí)狀態(tài)。
簡(jiǎn)單的實(shí)現(xiàn)
第一步,假設(shè)用戶(hù)訪(fǎng)問(wèn)了Shop或Office的任何一個(gè)頁(yè)面Any。該頁(yè)面所在的網(wǎng)站將會(huì)檢查用戶(hù)的即時(shí)狀態(tài)。如果用戶(hù)已經(jīng)登錄了,則將 Any頁(yè)面的信息返回給用戶(hù)。如果用戶(hù)還沒(méi)有登錄,則自動(dòng)轉(zhuǎn)到Service的Validate頁(yè)面,驗(yàn)證用戶(hù)在Service狀態(tài)。即Shop或 Office向Service發(fā)出請(qǐng)求,要求Service返回用戶(hù)的即時(shí)狀態(tài)。
第二步,Validate驗(yàn)證用戶(hù)的即時(shí)狀態(tài),如果 用戶(hù)已經(jīng)登錄了,則Service將用戶(hù)的即時(shí)狀態(tài)返回給Shop或Office的同步頁(yè)面 Synchronous,通知Shop或Office同步用戶(hù)狀態(tài)。如果用戶(hù)沒(méi)有登錄,則自動(dòng)轉(zhuǎn)向Customer頁(yè)面,提示用戶(hù)登錄。
第三步,用戶(hù)完成登錄過(guò)程,當(dāng)用戶(hù)成功登錄后,自動(dòng)轉(zhuǎn)回Validate頁(yè)面,通知Shop或Office的Synchronous進(jìn)行用戶(hù)狀態(tài)的同步。
第四步,在用戶(hù)狀態(tài)同步完成后,在本地站點(diǎn),用戶(hù)狀態(tài)成為在線(xiàn)狀態(tài),即可訪(fǎng)問(wèn)Any頁(yè)面。
在上面的流程中。我們知道,不管用戶(hù)訪(fǎng)問(wèn)哪個(gè)站點(diǎn),用戶(hù)只需要一次登錄,就保證用戶(hù)在Service的即時(shí)狀態(tài)都是在線(xiàn)的,不會(huì)再需要進(jìn)行第二次登錄的過(guò)程。
現(xiàn)在我們的思路已經(jīng)清楚,具體的實(shí)現(xiàn)我們將在代碼分析中完成。
代碼分析
從上面的流程中我們可以看出,系統(tǒng)中Shop和Office的代碼是完全類(lèi)似的。只要Shop可以實(shí)現(xiàn),Office也可以同樣的克隆。所以我們的重點(diǎn)分析的對(duì)象是Shop和Service的代碼。
1、Shop的Web.config和Project.cs
在Shop的Web.config里,我們配置了Service站點(diǎn)和Shop站點(diǎn),以方便我們?cè)诓渴饡r(shí)方便修改。
復(fù)制代碼 代碼如下:
<appsettings>
<add key="Service" value="http://localhost:8001" />
<add key="WebSite" value="http://localhost:8002" />
</appsettings>
在Project類(lèi)里進(jìn)行引用。
復(fù)制代碼 代碼如下:
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"];
}
}
2、Shop的Global.cs
Shop的Global.cs定義了四個(gè)Session變量,UserID用來(lái)標(biāo)識(shí)用 戶(hù)身份。Pass標(biāo)識(shí)用戶(hù)即時(shí)狀態(tài),Security用于保存往來(lái)Service和Shop的通訊不是被仿冒的。Url保存了上次請(qǐng)求的頁(yè)面,以保證在用 戶(hù)登錄后能轉(zhuǎn)到用戶(hù)請(qǐng)求的頁(yè)面。
復(fù)制代碼 代碼如下:
protected void Session_Start(Object sender, EventArgs e)
{
this.Session.Add("UserID", 0);
this.Session.Add("Pass", false);
this.Session.Add("Security", "");
this.Session.Add("Url", "");
}
3、Shop的Any.cs
Shop的Any.cs并沒(méi)有包含代碼,因?yàn)锳ny類(lèi)從Page繼承而來(lái),為了代碼分析方便,我們將代碼集中到Page.cs中。
復(fù)制代碼 代碼如下:
using System;
using System.Web;
namespace Amethysture.SSO.Shop
{
public class Any : Amethysture.SSO.Shop.Page
{
}
}
4、Shop的Page.cs
Page類(lèi)有兩個(gè)方法,CustomerValidate和Initialize。CustomerValidate用戶(hù)檢查用戶(hù)的即時(shí)狀態(tài),而Initialize是頁(yè)面登錄后發(fā)送給用戶(hù)的信息。我們的重點(diǎn)是CustomerValidate。
CustomerValidate是一個(gè)非常簡(jiǎn)單的流程,用條件語(yǔ)句檢查Pass的狀態(tài),如果Pass為否,則表示用戶(hù)沒(méi)有登錄,頁(yè)面跳轉(zhuǎn)到 Service的Validate頁(yè)面中。我們要分析的是其中保存的Url和遞交的WebSite和Security幾個(gè)參數(shù)。Url的作用在前面已經(jīng)講 清楚了,只是為了保證用戶(hù)登錄后能回到原來(lái)的頁(yè)面。而WebSite是為了保證該站點(diǎn)是被Service所接受的,并且保證Service知道是哪個(gè)站點(diǎn) 請(qǐng)求了用戶(hù)即時(shí)狀態(tài)。因?yàn)檫@個(gè)例子是個(gè)簡(jiǎn)單的例子,在后面的Validate里沒(méi)有驗(yàn)證WebSite是否是接受的請(qǐng)求站點(diǎn),但是在實(shí)際應(yīng)用中應(yīng)該驗(yàn)證這 一點(diǎn),因?yàn)镾hop和Service等同于服務(wù)器和客戶(hù)端,服務(wù)器出于安全考慮必須要檢查客戶(hù)端是否是被允許的。Security是非常重要的一點(diǎn)。 Shop對(duì)Service發(fā)送的是請(qǐng)求,不需要保證該請(qǐng)求沒(méi)有被篡改,但是在Service應(yīng)答Shop請(qǐng)求時(shí)就必須要保證應(yīng)答的數(shù)據(jù)沒(méi)有被篡改了。 Security正是為了保證數(shù)據(jù)安全而設(shè)計(jì)的。
在代碼中,Security是通過(guò)Hash一個(gè)隨機(jī)產(chǎn)生的數(shù)字生成的。具有不確定 性。和保密性。我們可以看到,Security同時(shí)保存在Session中和發(fā)送給Service。我們把這個(gè)Security當(dāng)作明文。在后面我們可以 看到,Security在Service經(jīng)過(guò)再一次Hash后作為密文發(fā)送回Shop。如果我們將Session保存的Security經(jīng)過(guò)同樣的 Hash方法處理后等到的字符串如果和Service返回的密文相同,我們就能夠在一定程度上保證Service應(yīng)答的數(shù)據(jù)是沒(méi)有經(jīng)過(guò)修改的。
復(fù)制代碼 代碼如下:
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(1, int.MaxValue).ToString();
byte[] Value;
UnicodeEncoding Code = new UnicodeEncoding();
byte[] Message = Code.GetBytes(Security);
SHA512Managed Arithmetic = new SHA512Managed();
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="0" height="0" 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();
}
}
}
5、Service的Global.cs
現(xiàn)在我們頁(yè)面轉(zhuǎn)到了Service的Validate頁(yè)面,我們轉(zhuǎn)過(guò)來(lái)看 Service的代碼。在Global中我們同樣定義了四個(gè)Session變量,都和Shop的Session用處類(lèi)似。WebSite是保存請(qǐng)求用戶(hù)即 時(shí)狀態(tài)的站點(diǎn)信息。以便能在登錄后返回正確的請(qǐng)求站點(diǎn)。
復(fù)制代碼 代碼如下:
protected void Session_Start(Object sender, EventArgs e)
{
this.Session.Add("UserID", 0);
this.Session.Add("Pass", false);
this.Session.Add("WebSite", "");
this.Session.Add("Security", "");
}
6、Service的Validate.cs
首先,將Shop傳遞過(guò)來(lái)的參數(shù)保存到Session中。如果用戶(hù)沒(méi)有登錄,則轉(zhuǎn)到Customer頁(yè)面進(jìn)行登錄。如果用戶(hù)已經(jīng)登錄了。則將用戶(hù)即時(shí)狀態(tài)傳回給Shop站點(diǎn)。如上所述,這里將Security重新Hash了一次傳回給Shop,以保證數(shù)據(jù)不被纂改。
復(fù)制代碼 代碼如下:
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);
SHA512Managed Arithmetic = new SHA512Managed();
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");
}
}
7、Service的Customer.cs和Login.cs
Customer主要的是一個(gè)用于登錄的表單,這里就不 貼出代碼了。這里分析一下Login的一段代碼,這段代碼是當(dāng)?shù)卿浭侵苯釉赟ervice完成的(WebSite為空值),則頁(yè)面不會(huì)轉(zhuǎn)到Shop或 Office站點(diǎn)。所以應(yīng)該暫停在Service站點(diǎn)。系統(tǒng)如果比較完美,這里應(yīng)該顯示一組字系統(tǒng)的轉(zhuǎn)向鏈接。下面我們看到,當(dāng)Pass為真時(shí),頁(yè)面轉(zhuǎn)回 到Validate頁(yè)面,通過(guò)上面的分析,我們知道,頁(yè)面會(huì)轉(zhuǎn)向Shop的Synchronous頁(yè)面,進(jìn)行用戶(hù)狀態(tài)的同步。
復(fù)制代碼 代碼如下:
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");
}
8、Shop的Synchronous.cs
好了,我們?cè)赟ervice中完成了登錄,并把用戶(hù)狀態(tài)傳遞回Shop站 點(diǎn)。我們接著看用戶(hù)狀態(tài)是怎么同步的。首先,如果Session里的Security是空字符串,則表示Shop站點(diǎn)沒(méi)有向Service發(fā)送過(guò)請(qǐng)求,而 Service向Shop發(fā)回了請(qǐng)求,這顯然是錯(cuò)誤的。這次訪(fǎng)問(wèn)是由客戶(hù)端偽造進(jìn)行的訪(fǎng)問(wèn),于是訪(fǎng)問(wèn)被拒絕了。同樣Security和 InSecurity不相同,則表示請(qǐng)求和應(yīng)答是不匹配的??赡軕?yīng)答被纂改過(guò)了,所以應(yīng)答同樣被拒絕了。當(dāng)檢驗(yàn)Security通過(guò)后,我們保證 Serive完成了應(yīng)答,并且返回了確切的參數(shù),下面就是讀出參數(shù)同步Shop站點(diǎn)和Service站點(diǎn)的用戶(hù)即時(shí)狀態(tài)。
復(fù)制代碼 代碼如下:
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);
SHA512Managed Arithmetic = new SHA512Managed();
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("數(shù)據(jù)錯(cuò)誤");
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("訪(fǎng)問(wèn)錯(cuò)誤");
this.Response.Write("");
this.Response.Write("");
this.Response.Write("");
}
9、Shop的Page.cs
我們知道,頁(yè)面在一段時(shí)間不刷新后,Session會(huì)超時(shí)失效,在我們一直訪(fǎng)問(wèn)Shop的 時(shí)候怎么才能保證Service的Session不會(huì)失效呢?很簡(jiǎn)單,我們返回來(lái)看Shop的Page.cs。通過(guò)在所有Shop的頁(yè)面內(nèi)都用 <iframe>嵌套Service的某個(gè)頁(yè)面,就能保證Service能和Shop的頁(yè)面同時(shí)刷新。需要注意的一點(diǎn)是Service的Session必 須保證不小于所有Shop和Office的Session超時(shí)時(shí)間。這個(gè)在Web.config里可以進(jìn)行配置。
復(fù)制代碼 代碼如下:
this.Response.Write("<iframe width="0" height="0" src="" + project.service + "/Customer.aspx"></iframe>");
總結(jié)
一次完整的登錄完成了。我們接著假設(shè)一下現(xiàn)在要跳到Office的Any頁(yè)面,系統(tǒng)會(huì)進(jìn)行怎樣的操作 呢?Any(用戶(hù)沒(méi)有登錄)->Validate(用戶(hù)已經(jīng)登錄)->Synchronous(同步)->Any。也就是說(shuō)這次,用戶(hù)沒(méi)有進(jìn)行登錄的過(guò) 程。我們通過(guò)一次登錄,使得Service的用戶(hù)狀態(tài)為登錄,并且不管有多少個(gè)網(wǎng)站應(yīng)用,只要這些網(wǎng)站都保證符合Shop的特性,這些網(wǎng)站就都能保持 Service的用戶(hù)狀態(tài),同時(shí)能通過(guò)Service獲得用戶(hù)的狀態(tài)。也就是說(shuō)我們實(shí)現(xiàn)了SSO
相關(guān)文章
C#實(shí)現(xiàn)定時(shí)任務(wù)Task Scheduler的示例代碼
這篇文章主要為大家詳細(xì)介紹了C#實(shí)現(xiàn)定時(shí)任務(wù)Task Scheduler的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-02-02如何使用Dapper處理多個(gè)結(jié)果集與多重映射實(shí)例教程
Dapper類(lèi)是一個(gè)開(kāi)源的數(shù)據(jù)庫(kù)操作類(lèi),下面這篇文章主要給大家介紹了關(guān)于如何使用Dapper處理多個(gè)結(jié)果集與多重映射的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-09-09C#實(shí)現(xiàn)網(wǎng)絡(luò)小程序的步驟詳解
經(jīng)常要檢測(cè)某些IP地址范圍段的計(jì)算機(jī)是否在線(xiàn)。有很多的方法,比如進(jìn)入到網(wǎng)關(guān)的交換機(jī)上去查詢(xún)、使用現(xiàn)成的工具或者編寫(xiě)一個(gè)簡(jiǎn)單的DOS腳本等等,這些都比較容易實(shí)現(xiàn)。本文將用C#來(lái)實(shí)現(xiàn),感興趣的可以了解一下2022-12-12詳解c# 強(qiáng)制轉(zhuǎn)換和類(lèi)型轉(zhuǎn)換
這篇文章主要介紹了c# 強(qiáng)制轉(zhuǎn)換和類(lèi)型轉(zhuǎn)換的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)c#,感興趣的朋友可以了解下2020-10-10