这篇文章介绍了
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