您的位置:首页 > 其它

分布式系统用户统一认证浅析(二)认证中心主动认证实现

2010-11-28 09:54 471 查看
上一篇文章写了被动认证的实现,本文接着写主动认证的实现。

一、认证流程

为了和被动认证有个比较,这些列出被动认证的流程图

1.1认证(图1为被动认证,图2为主动认证):





主动认证的流程和被动的区别是,在每次登录后,主动发通知到子站点A,告诉子站点A已经登录,并且拥有Token。这种方法的前提是,每次用户退出时,认证中心也会主动通知所有的子系统用户已经退出,清除该Token。这种认证方法有一个好处,减少了认证中心和子站点之间的交互次数,提高了性能。

二、认证实现(C#,VS2008)

2.1、认证实现

首先,建立认证中心的Web站点,页面机构如下:



子站点SubWebSite工程,页面结构如下:



修改一下上边的验证示意图,得到页面调用顺序示意图:



当用户请求子系统页面(Default.aspx),则该用户是否已经有了Token(也就是Session[“Token”]),若没有,则跳转到用户中心进行登录,登录之后,用户中心发送通知,告知子系统用户已经登录(SSO.aspx设置Seesion[“Token”]),通知结束后进行跳转回子系统Default.aspx页面,此时已经有Token,可以拿此Token去验证中心获取用户信息,并显示页面信息给用户。

通知部分使用js通知(如下):

<script type="text/javascript" src="jquery.min.js"></script>
<%--通知子系统用户登录了(这里要对每个子系统进行通知)--%>
<script type="text/javascript" src="http://mowen:8000/subwebsite/SSO.aspx<%=ssoParas%>"></script>
<script type="text/javascript">
$(document).ready( //使用$(document).ready的作用是,通知请求返回之后再跳转
function() {
var returnUrl = '<%=returnUrl%>';
if ($.trim(returnUrl) != null) {
//通知结束后,跳转到子站点
location.href = returnUrl;
}
}
);
</script>

<script type="text/javascript" src="jquery.min.js"></script>
<%--通知子系统用户退出了(这里要对每个子系统进行通知)--%>
<script type="text/javascript" src="http://mowen/usercenter/SSO.aspx<%=ssoParas%>"></script>
<script type="text/javascript">
$(document).ready(
function() {
//通知结束后,跳转到登录页面
location.href = "SignIn.aspx?ReturnUrl=<%=returnUrl %>";
}
);
</script>
退出通知结束后跳转到登录页面。

来看实现代码:

子系统subWebSite:Default.aspx(子系统需要认证的页面)
<form id="form1" runat="server">
<div>
通过了认证,登录的用户名为<%=Session["UserName"] %>
<a href="http://mowen:8000/UserCenter/SignOut.aspx?ReturnUrl=<%HttpUtility.UrlEncode(Request.Url.AbsoluteUri) %>">退出(此链接为验证中心的链接(退出页面))</a>
</div>
</form>

子系统SubWebSite:Default.aspx.cs

protected void Page_Load(object sender, EventArgs e)
{
//当前子系统登录,且没有从认证中心取得Token
if (Session["Token"] == null)
ReSignIn();//需要重新登录
//是否获取得到了用户信息
if (Request["UserName"] != null)
Session["UserName"] = Request["UserName"];
//有了Token,但还没有用户信息,跳转到平台获取用户信息
if (Session["Token"] != null && Session["UserName"] == null)
{
//已经从认证中心取得Token,根据Token取认证中心取得用户信息
Response.Redirect("Http://mowen:8000/UserCenter/GetUserInfo.aspx?ReturnUrl=" +
HttpUtility.UrlEncode(Request.Url.AbsoluteUri) + "&Token=" + Session["Token"]);
}
}
/// <summary>
/// 重新登录获取Token
/// </summary>
protected void ReSignIn()
{
Response.Redirect("http://mowen:8000/UserCenter/Default.aspx?ReturnUrl=" + HttpUtility.UrlEncode(Request.Url.AbsoluteUri));
}
SubWebSite:Sso.aspx.cs(子系统处理通知的页面)

protected void Page_Load(object sender, EventArgs e)
{
if(Request["l"] != null)//通知类型
{
string type = Request["l"];
//若通知的是登录
if (type == "signin")
{
if (Request["Token"] != null)
{
Session["Token"] = Request["Token"];
}
}
//若是退出,则清空Session
else if(type=="signout")
{
Session.Clear();
}
Response.Write("alert('通知类型为" + type + ",Token为" + Request["Token"] + "');");
}
Response.End();
}
认证中心获取Token页面(Default.aspx,此页面主要处理通知,若用户已经登录则进行通知,若没有登录则跳转到登录页面):

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script type="text/javascript" src="jquery.min.js"></script>
<%--通知子系统用户登录了(这里要对每个子系统进行通知)--%>
<script type="text/javascript" src="http://mowen:8000/subwebsite/SSO.aspx<%=ssoParas%>"></script>
<script type="text/javascript">
$(document).ready(
function() {
var returnUrl = '<%=returnUrl%>';
if ($.trim(returnUrl) != null) {
//通知结束后,跳转到子站点
location.href = returnUrl;
}
}
);
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
</div>
</form>
</body>
</html>
认证中心获取Token页面(Default.aspx.cs)
public string returnUrl = string.Empty;//记录需要往回跳转的url
public string ssoParas = string.Empty;//通知的参数,若用户已经登录,此参数列表会用到
protected void Page_Load(object sender, EventArgs e)
{
//来源的Url(此Http请求上一个请求,由上一个请求使用参数传递)
if (Request["ReturnUrl"] != null)
returnUrl = HttpUtility.HtmlDecode(Request["ReturnUrl"].Trim());
//根据Session来判断用户是否已经登录
if(Session["UserName"] != null && Session["Token"] != null)
{
ssoParas = "?Token=" + Session["Token"] + "&UserName=" + Session["UserName"] + "&l=signin";
}
else
{
//没有登录,跳转到登录页面
Response.Redirect("SignIn.aspx?ReturnUrl=" + HttpUtility.UrlEncode(returnUrl));
}
}
认证中心登录页面:

SignIn.aspx:
<form id="form1" runat="server">
<div>
用户名:<asp:TextBox ID="txtUserName" runat="server"></asp:TextBox><br />
密码:<asp:TextBox ID="txtPwd" runat="server"></asp:TextBox><br />
<asp:Button ID="btnSignIn" runat="server" Text="登录" onclick="btnSignIn_Click" />
</div>
</form>
SignIn.aspx.cs:
protected void Page_Load(object sender, EventArgs e) { }
protected void btnSignIn_Click(object sender, EventArgs e)
{
Session["UserName"] = txtUserName.Text.Trim();
Session["Token"] =
System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(
txtUserName.Text.Trim() + DateTime.Now, "md5"); //*************************************************************************
//将Token和用户信息关联起来
Hashtable htTokenInfo = null;
if (Application["UserTokenInfo"] != null)
htTokenInfo = (Hashtable)Application["UserTokenInfo"];
if (htTokenInfo == null)
htTokenInfo = new Hashtable();
if (!htTokenInfo.ContainsKey(Session["Token"]))
htTokenInfo.Add(Session["Token"], txtUserName.Text.Trim());
else
htTokenInfo[Session["Token"]] = txtUserName.Text.Trim();
Application["UserTokenInfo"] = htTokenInfo; //*************************************************************************
//来源的Url(此Http请求上一个请求,由上一个请求使用参数传递)
var returnUrl = string.Empty;
if (Request["ReturnUrl"] != null)
returnUrl = Request["ReturnUrl"].Trim();
//跳转Default.aspx进行通知
Response.Redirect("Default.aspx?ReturnUrl=" + returnUrl);
}
/// <summary>
/// 功能:url里有key的值,就替换为value,没有的话就追加.
/// 执行过程:(1)、使用正则表达式匹配key
/// (2)、将匹配的Key的值替换为空的值
/// (3)、使用新值进行替换
/// 作者:莫文
/// 时间:2010-5-30 13:48
///
/// </summary>
/// <param name="url">要替换Url参数值的Url</param>
/// <param name="paramText">要替换值的参数名</param>
/// <param name="paramValue">要被替换的参数的值</param>
/// <returns>替换参数值后的Url</returns>
public static string BuildUrl(string url, string paramText, string paramValue)
{
//使用正则匹配所要查找的Key
Regex reg = new Regex(string.Format("{0}=[^&]*", paramText), RegexOptions.IgnoreCase);
Regex reg1 = new Regex("[&]{2,}", RegexOptions.IgnoreCase);
//将旧值替换为空
string _url = reg.Replace(url, "");
//新值
if (_url.IndexOf("?") == -1)
_url += string.Format("?{0}={1}", paramText, paramValue);//?
else
_url += string.Format("&{0}={1}", paramText, paramValue);//&
//将新值替换到Url中
_url = reg1.Replace(_url, "&");
//将多余的&字符清除
_url = _url.Replace("?&", "?");
return _url;
}
认证中心验证Token页面(GetUserInfo.aspx.cs):
protected void Page_Load(object sender, EventArgs e)
{
Hashtable htTokenInfo = null;
if (Application["UserTokenInfo"] != null)
htTokenInfo = (Hashtable)Application["UserTokenInfo"];
//来源的Url(此Http请求上一个请求,由上一个请求使用参数传递)
var returnUrl = string.Empty;

if (Request["ReturnUrl"] != null)
returnUrl = HttpUtility.HtmlDecode(Request["ReturnUrl"].Trim());
string token = Request["Token"];
if (htTokenInfo == null || string.IsNullOrEmpty(token))
{
//没有登录,跳转到登录页面
Response.Redirect("SignIn.aspx?eturnUrl=" + HttpUtility.UrlEncode(returnUrl));
}
else
{
//用户信息是否和Session关联
if (htTokenInfo[token] == null)
//没有登录,跳转到登录页面
Response.Redirect("SignIn.aspx?ReturnUrl=" + HttpUtility.UrlEncode(returnUrl));
//附加上Token参数
if (returnUrl.IndexOf("?") > 0)
returnUrl = returnUrl + "&UserName=" + htTokenInfo[token];
else
returnUrl = returnUrl + "?UserName=" + htTokenInfo[token];
//跳回原页面
Response.Redirect(returnUrl);
}
}

认证中心验证退出页面(SignOut.aspx):

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script type="text/javascript" src="jquery.min.js"></script>
<%--通知子系统用户退出了(这里要对每个子系统进行通知)--%>
<script type="text/javascript" src="http://mowen/usercenter/SSO.aspx<%=ssoParas%>"></script>
<script type="text/javascript">
$(document).ready(
function() {
//通知结束后,跳转到登录页面
location.href = "SignIn.aspx?ReturnUrl=<%=returnUrl %>";
}
);
</script></head>
<body><form id="form1" runat="server"><div></div></form></body></html>
SignOut.aspx.cs:
public string returnUrl = string.Empty;//往回跳转地址
public string ssoParas = string.Empty;//通知参数
protected void Page_Load(object sender, EventArgs e)
{
//根据Session来判断用户是否已经登录
if (Session["UserName"] != null && Session["Token"] != null)
{
ssoParas = "?Token=" + Session["Token"] + "&UserName=" + Session["UserName"] + "&l=signout";
}
//清空Session
Session.Clear();
if (Request["ReturnUrl"] != null)
returnUrl = Request["ReturnUrl"].Trim();
}

咱们来看看效果图:
在我机器上部署了两个站点(我使用虚拟目录模式):
(1)、http://mowen:8000/userCenter/
(2)、http://mowen:8000/subwebsite/
首先请求子系统:



跳转到用户中心登录:



登录:



用户中心通知子系统:



确定后跳转回到子系统:



点击退出,则跳转回到用户中心:



实现代码下载:http://www.cnfounder.com/mw/UserCenterDemo2.rar
同样退出(退出链接在子系统中指向验证中心的退出页面,原因是由验证中心来统一管理用户)的时候,也是需要通知的:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐