單點登陸1

系統的基本架構
   咱們假設一個系統System包含Service客戶服務中心、Shop網上購物中心和Office網上辦公中心三個獨立的網站。 Service管理客戶的資料,登陸和註銷過程。不論客戶訪問System的任何一個頁面,系統都會轉到登陸界面,在用戶登陸後,系統會自動轉會到客戶上 次請求的頁面。而且用戶此後能夠在System中無縫切換。不須要再次進行登陸。即在System中實現單點登陸SSO(Single Sign-On)。
  咱們知道,用戶的即時狀態一般是使用Application、Session、Cookie和存儲的。而這些都是不能在程序中跨站點訪問的。咱們必需經過站點間相互通信來確認用戶的即時狀態。
 簡單的實現
   第一步,假設用戶訪問了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的Web.config和Project.cs
  在Shop的Web.config裏,咱們配置了Service站點和Shop站點,以方便咱們在部署時方便修改。
複製代碼 代碼以下:

<appsettings>
<add key="Service" value="http://localhost:8001" />
<add key="WebSite" value="http://localhost:8002" />
</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的Global.cs
   Shop的Global.cs定義了四個Session變量,UserID用來標識用 戶身份。Pass標識用戶即時狀態,Security用於保存往來Service和Shop的通信不是被仿冒的。Url保存了上次請求的頁面,以保證在用 戶登陸後能轉到用戶請求的頁面。
複製代碼 代碼以下:

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", "");
}

  三、Shop的Any.cs
  Shop的Any.cs並無包含代碼,由於Any類從Page繼承而來,爲了代碼分析方便,咱們將代碼集中到Page.cs中。
複製代碼 代碼以下:

using System;
using System.Web;
namespace Amethysture.SSO.Shop
{
 public class Any : Amethysture.SSO.Shop.Page
 {
 }
}

  四、Shop的Page.cs
  Page類有兩個方法,CustomerValidate和Initialize。CustomerValidate用戶檢查用戶的即時狀態,而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(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();
  }
 }
}

五、Service的Global.cs
   如今咱們頁面轉到了Service的Validate頁面,咱們轉過來看 Service的代碼。在Global中咱們一樣定義了四個Session變量,都和Shop的Session用處相似。WebSite是保存請求用戶即 時狀態的站點信息。以便能在登陸後返回正確的請求站點。
複製代碼 代碼以下:

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", "");
}

  六、Service的Validate.cs
  首先,將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);
  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");
 }
}

  七、Service的Customer.cs和Login.cs
   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的Synchronous.cs
   好了,咱們在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);
 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("數據錯誤");
  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的Page.cs
   咱們知道,頁面在一段時間不刷新後,Session會超時失效,在咱們一直訪問Shop的 時候怎麼才能保證Service的Session不會失效呢?很簡單,咱們返回來看Shop的Page.cs。經過在全部Shop的頁面內都用 <iframe>嵌套Service的某個頁面,就能保證Service能和Shop的頁面同時刷新。須要注意的一點是Service的Session必 須保證不小於全部Shop和Office的Session超時時間。這個在Web.config裏能夠進行配置。
複製代碼 代碼以下:

this.Response.Write("<iframe width="0" height="0" src="" + project.service + "/Customer.aspx"></iframe>");

總結
   一次完整的登陸完成了。咱們接着假設一下如今要跳到Office的Any頁面,系統會進行怎樣的操做 呢?Any(用戶沒有登陸)->Validate(用戶已經登陸)->Synchronous(同步)->Any。也就是說此次,用戶沒有進行登陸的過 程。咱們經過一次登陸,使得Service的用戶狀態爲登陸,而且無論有多少個網站應用,只要這些網站都保證符合Shop的特性,這些網站就都能保持 Service的用戶狀態,同時能經過Service得到用戶的狀態。也就是說咱們實現了SSOcss

相關文章
相關標籤/搜索