我的知识库

知识等于力量

« js小技巧使用dom4j的xPath解析XML »

XSSO(多方单点认证)技术规范

一、       概述... 1

二、       XSSO母系统和XSSO子系统的概念... 1

三、       XSSO基本原理描述... 1

四、       XSSO母系统必须实现的功能、实现方法... 2

五、       XSSO子系统必须实现的功能、实现方法... 3

六、       XSSO案例分析... 4

七、       关于XSSO的发起人方面的信息... 5

八、       其他说明... 5

九、       附录1:案例母系统xssouser.aspx. 5

十、       附录2:案例子系统xssoenter.aspx. 7

 

一、            概述

XSSO是一个基于www的关于用户认证方面的技术规范,主要目标是以最简单的方式实现网站注册用户的注册位置无关性,注册位置无关性的意思是:用户在任何支持XSSO规范的网站注册后可以无须重复注册即可登陆其他支持XSSO规范的网站。

二、            XSSO母系统和XSSO子系统的概念

l         XSSO母系统:为用户保存帐号、密码等帐户信息的系统,是一个具有一定数量注册用户的独立网站,它可以根据需要将一个或多个具有不同功能的XSSO子系统纳入自己的业务范围,从而为自己的注册用户提供更丰富的服务,这些注册用户不需要在子系统进行重复的帐户信息注册,就可以个性化地使用子系统提供的服务。

l         XSSO子系统:是一个不保存用户帐户信息但可以为多个XSSO母系统的注册用户提供个性化服务的独立网站。

l         一个网站可以只是XSSO母系统,也可以只是XSSO子系统,也可以既是XSSO母系统又是XSSO子系统。

三、            XSSO基本原理描述

l         认 证一个用户是不是他自己,最有发言权的是用户自己,在现实生活中有这样的一个例子:一个手机用户到电信网站查询自己的手机信息,电信网站为了认证是不是用 户自己在查询,会发一条密码到用户的手机,只有用户自己的手机可以收到这条密码,利用密码就可以查看自己的信息,此认证过程就好象是用户自己在认证自己一 样;XSSO的认证方法正是类似这种用户自己认证自己的方式,当母系统的用户要求登录子系统时,子系统并没有保存有此用户的帐户信息,无法认证用户,但子系统可以询问用户所在的母系统,了解当前用户在母系统的认证情况,从而判断当前用户是否已通过了认证。

l         取名为XSSO(多方单点认证),可以体现两个方面的意思,一方面,对于一个确定的用户而言,可以利用自己注册的母系统为多个自己使用的子系统提供单点认证,不管有多少个子系统,认证点始终只有一个,这体现了单点认证的概念(即:SSO)。另一方面,对于一个确定的子系统而言,它需要依赖保存了用户帐户信息的母系统来完成认证过程,而子系统的许多用户可能分别位于多个相互独立的母系统,这体现了一个子系统依赖多个认证点的特性,用X表示(即:XSSO)。也可以把X理解为Experience(体验)

l         因为子系统必须跨域询问当前用户在母系统的认证状态,而此认证状态是保存在母系统的会话中的,所以XSSO认证的难点是跨域共享会话数据,要跨域共享Session数据,通常的做法是在应用程序的状态库、缓存或数据库中保存票据化的Session数据,再以票据号来提取,但这样做会使母系统的代码修改量增大,甚至要修改数据库,不适合对现有系统的改造;本规范以尽可能减少母系统代码修改量为原则,同时以尽可能少依赖服务器技术为原则,采用一种另类方式,方法是:在子系统客户会话中以js文件方式跨域导入母系统当前会话的Session数据,以达到共享Session数据的目的。

四、            XSSO母系统必须实现的功能、实现方法

l         具有完整的用户注册及用户管理方面的功能。

l         为每个注册用户分别提供一个全球唯一的XSSO用户名片路径(xssouserurl),它是一个标准的Url路径,以字符串的方式提供给XSSO子系统使用,此字符串的格式如:“http://xxx.xxx.xxx/xssouser.aspx?username=xxx&js=0 ,此路径的参数部分必须包含“js=0”字符串,母系统自己利用此路径必须能够识别属于哪一个用户(比如利用参数username=xxx就可以识别具体所属用户,但识别的方式不局限于此);如果参数js=0则此路径可以象名片一样展示用户相关的联系信息(母系统可以设计成让用户自己来编辑这些信息),如果js=1则此路径用于子系统来询问用户是否在母系统当前会话已通过认证,母系统利用此页面将用户的认证状态以js脚本的方式共享给子系统。

l         ASP.NET为例,当js=1时,名片页面可以参考以下代码进行内容输出:

protected void Page_Load(object sender, EventArgs e)

{

    if (Request.QueryString["username"] != null && Request.QueryString["js"] != null)

    {

        string sUserId = Request.QueryString["username"];

        string sJs = Request.QueryString["js"];

        if (sJs == "1")

        {

            string sAuthOk = "0";// 0 is not authenticated or is not this user.

            string sAuthName = "";

            //*** Is authenticated ? ***

            if (Request.IsAuthenticated)

            {

                //*** Is this user ? ***

                if (User.Identity.Name == sUserId)

                {

                    sAuthOk = "1";// 1 authenticated and is this user.

                    sAuthName = User.Identity.Name;

                }

            }

            // Response write script as "xssoCall(sAuthOk, sAuthName);".

            string sScript = "xssoCall('" + sAuthOk + "','" + sAuthName + "');";

            Response.Clear();

            Response.ContentEncoding = System.Text.Encoding.UTF8;

            Response.ContentType = "text/javascript";

            Response.Write(sScript);

            Response.End();

        }

        else

        {// show user info

        }

    }

}

注意:当js=1时必须输出一个“xssoCall(sAuthOk,sAuthName)”的js方法调用,如上图所示,需要做两个判断,一是当前会话是否已认证,二是认证用户是否是当前询问路径所代表的用户,只要有一个条件不成立就输出sAuthOk 为“0”,如果两个条件都成立则输出sAuthOk为“1”同时输出认证用户的用户名称。

l         另外,母系统还应该为登录成功的用户提供一个可以导航到子系统入口页面的链接(xssoenterurl),此入口页面路径是由子系统为母系统提供的。

五、            XSSO子系统必须实现的功能、实现方法

l         为母系统的用户公开子系统的入口页面路径“xssoenterurl”的字符串,此路径字符串的参数部分必须包含“xssouserurl=xssouserurlencode”子字符串,格式如:“http://xxx.xxx.xxx/xssoenter.aspx? xssouserurl=xssouserurlencode”,当母系统在使用此路径为用户提供链接时需要将“xssouserurl=xssouserurlencode”中的xssouserurlencode替换为登录用户的名片路径xssouserurlURL编码字符串。

l         子系统需要设计一个成员数据表,在成员数据表中以用户的名片路径xssouserurl作为用户标记,保存时最好将xssouserurlURL编码并全部转换为小写字母,因为xssouserurl是全球唯一的,所以子系统可以放心地利用它来标记来自任何母系统的用户。另外,子系统可以自由地设计成员数据表的其他字段以实现更丰富的成员个性化功能,但本协议不做规定。

l         登录子系统的过程是:母系统的注册用户,使用母系统的登录表单登录母系统,之后使用子系统入路径xssoenterurl导航到子系统的入口页面,此时登录用户实际上从母站点转向了另一个网站(子系统),子系统入口页面获取xssouserurl参数后,将xssouserurl中的“js=0”换为“js=1”,并把此xssouserurl路径当作js文件导入子系统的入口页面客户端,这样就相当于在子系统的当前会话传入母系统当前会话的数据,子系统入口页面需要在客户端提供一个javascript方法:xssoCall(sAuthOk,sAuthName)接受母系统用户名片页面xssouserurl的调用,这个xssoCall(sAuthOk,sAuthName)方法主要工作是将母系统的认证状态写入子系统的会话,从而子系统就取到了当前用户在母系统当前会话的认证状态,如果用户在母系统的当前会话已通过认证,则子系统查询自己的成员数据库看以xssouserurl为标记的用户是否已存在,如果成员数据库中存在以xssouserurl为标记的用户数据,则利用xssouserurl标记子系统当前会话为认证状态;如果成员数据库不存在以xssouserurl为标记的用户数据,则表示此用户尚未开通本子系统,可以引导用户做一些开通工作,开通的主要工作是在成员数据表保存以xssouserurl为标记的用户数据,保存后再利用xssouserurl标记子系统当前会话为认证状态;如果询问母系统的结果是未认证,则引导用户以匿名的方式访问子系统。

l         另外,子系统可以做更多的安全方面的考虑或自由地设计授权方面的功能(比如基于角色的授权),但本协议不做规定。

六、            XSSO案例分析

l         案例说明:本案例采用ASP.NET简单演示XSSO母系统和XSSO子系统的实现过程,母系统只有一个页面xssouser.aspx,子系统也只有一个页面xssoenter.aspx;可以把这两个页面分别复制到两个不同域的asp.net站点完成测试,假设母系统部署在a.com,子系统部署在b.com,则用户“user1”在母系统的名片路径是:http://www.a.com/xssouser.aspx?username=user1&js=0,子系统的入口路径是:http://www.b.com/xssoenter.aspx?xssouserur=xssouserurencode;此案例实现的主要功能是:访问母系统http://www.a.com/xssouser.aspx可以在表单中输入帐号和密码进行注册或登录,登录后会显示子系统的入口连接列表,点击子系统的入口连接即可登录到子系统http://www.b.com,登录子系统后可以编辑留言信息,测试时可以利用一个帐号重复登录到子系统编辑和修改留言。在这个案例中用户的帐号和密码保存在a.com中,而b.com并没有保存用户的帐号密码。对于a.com而言可以为用户提供多个b.com这样的子系统以提供多样化服务;对于b.com而言可以为多个a.com这样的用户系统提供同样的服务。

l         母系统xssouser.aspx的代码:见本文附录1

l         子系统xssoenter.aspx的代码: 见本文附录2

 

七、            关于XSSO的发起人方面的信息

姓名:王晓飞

职业:自由程序员

Email: chinaxiaofei@163.com

Blog: http://chinaxiaofei.cnblogs.com

八、            其他说明

本技术规范是免费的,朋友们在发布或引用时请注明来源。特此感谢!

 

2006-11-6

chianxiaofei@163.comhttp://www.bokehou.com,王晓飞

 

 

九、            附录1:案例母系统xssouser.aspx

<%@ Page Language="C#" %>

<script runat="server">

//***请根据实际情况在此设置子系统的入口路径****

private string _XssoEnterUrl = "http://www.b.com/xssoenter.aspx?xssouserurl=xssouserurlencode";

//*******************************

protected void Page_Load(object sender, EventArgs e)

{

    if (Request.QueryString["username"] != null && Request.QueryString["js"] != null)

    {

        string sUserId = Request.QueryString["username"];

        string sJs = Request.QueryString["js"];

        if (sJs == "1")

        {

            string sAuthOk = "0";// 0 is not authenticated or is not this user.

            string sAuthName = "";

            //*** Is authenticated ? ***

            if (Request.IsAuthenticated)

            {

                //*** Is this user ? ***

                if (User.Identity.Name == sUserId)

                {

                    sAuthOk = "1";// 1 authenticated and is this user.

                    sAuthName = User.Identity.Name;

                }

            }

            // Response write script as "xssoCall(sAuthOk, sAuthName);".

            string sScript = "xssoCall('" + sAuthOk + "','" + sAuthName + "');";

            Response.Clear();

            Response.ContentEncoding = System.Text.Encoding.UTF8;

            Response.ContentType = "text/javascript";

            Response.Write(sScript);

            Response.End();

        }

        else

        {// show user info

        }

    }

}

protected void btn_Submit_Click(object sender, EventArgs e)

{

    string sUserName = this.tb_UserName.Text;

    string sPassword = this.tb_Password.Text;

    if (sUserName == "" || sPassword == "")

    {

        Response.Write("帐号和密码不能为空!");

    }

    else

    {

        if (Application[sUserName] == null)

        {//未注册

            Application[sUserName] = sPassword;

        }

        if (sPassword == Application[sUserName].ToString())

        {//密码正确,登录

            System.Web.Security.FormsAuthentication.SetAuthCookie(sUserName, false);

            //显示子系统列表

            Response.Write("用户:" + sUserName + "&nbsp;" + MakeEntersHtml(sUserName));

        }

        else

        {

            Response.Write("密码错误!");

        }

    }

}

private string MakeEntersHtml(string sUserName)

{

    //XssoUserUrl,格式如:http://www.a.com/xssouser.aspx?username=user1&js=0

    string sXssoUserUrl = Request.Url.ToString();

    if (sXssoUserUrl.IndexOf("?") > 0)

    {

        sXssoUserUrl = sXssoUserUrl.Substring(0, sXssoUserUrl.IndexOf("?"));

    }

    sXssoUserUrl = Server.UrlEncode(sXssoUserUrl + "?username=" + sUserName + "&js=0");

    //XssoEnterUrl,格式如:http://www.b.com/xssoenter.aspx?xssouserurl=xssouserurlencode

    string sEnterUrl = _XssoEnterUrl.Replace("xssouserurlencode", sXssoUserUrl);

    return "&nbsp;&nbsp;<a target='_blank' href='" + sEnterUrl + "'>登录子系统1</a>";

}

</script>

<html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>XSSO母系统演示-chinaxiaofei.cnblogs.com-XSSO轻磕Web3.0</title>

</head>

<body>

    <form id="form1" runat="server">

    <div style="white-space:nowrap;">

    用户:<asp:TextBox runat="server" ID="tb_UserName" Width="80"></asp:TextBox>

    密码:<asp:TextBox runat="server" ID="tb_Password" Width="80"></asp:TextBox>

    <asp:Button runat="server" ID="btn_Submit" Text="注册或登录" OnClick="btn_Submit_Click" />

    </div>

    </form>

</body>

</html>

 

十、            附录2:案例子系统xssoenter.aspx

<%@ Page Language="C#" %>

<script runat="server">

    protected void Page_Load(object sender, EventArgs e)

    {

        if (!Page.IsPostBack)

        {

            if (Request.QueryString["xssouserurl"] != null)

            {//page1:获取母系统的会话信息

                string sXssoUserUrl = Server.UrlDecode(Request.QueryString["xssouserurl"]);

                this.Session["demo_xssouserurl"] = sXssoUserUrl;

                if (sXssoUserUrl.IndexOf("js=0") > 0)

                {

                    string sScript = "<script type='text/javascript'>";

                    sScript += "function xssoCall(sAuthOk,sAuthName){";

                    sScript += "if(sAuthOk == '1'){if(sAuthName != ''){";

                    sScript += "document.cookie = 'demo_username='+escape(sAuthName);}}";

                    sScript += "document.location.href = '" + Request.Url.AbsolutePath + "';";

                    sScript += "}<" + "/script><script type='text/javascript' src='";

                    sScript += sXssoUserUrl.Replace("js=0", "js=1") + "&time=" + DateTime.Now.Ticks.ToString();

                    sScript += "'><" + "/script>";

                    Response.Write(sScript);

                }

                else

                {

                    Response.Write("不合法的MSSO用户名片路径!");

                }

            }

            else if (Session["demo_xssouserurl"] != null)

            {//page2:用户在子系统注册和登录

                HttpCookie u = Request.Cookies["demo_username"];

                if (u != null && u.Value != "")

                {//用户在母系统已认证

                    string sUserName = Server.UrlDecode(u.Value);

                    string sUserId = Server.UrlEncode(Session["demo_xssouserurl"].ToString().ToLower());

                    if (Application[sUserId] == null)

                    {//如果用户在成员数据库中不存在,将用户添加到成员数据库

                        Application[sUserId] = "欢迎:" + sUserName + "|" + DateTime.Now.ToString();

                    }

                    //登录本子系统

                    System.Web.Security.FormsAuthentication.SetAuthCookie(sUserId, false);

                    //清空临时数据

                    Session.Remove("demo_xssouserurl");

                    Response.Cookies.Remove("demo_username");

                    //转向资源页面

                    Response.Redirect(Request.Url.AbsolutePath + "?username=" + Server.UrlEncode(sUserName), true);

                }

                else

                {//匿名访问

                    //清空临时数据

                    Session.Remove("demo_xssouserurl");

                    Response.Cookies.Remove("demo_username");

                    //

                    Response.Write("此用户没有通过认证!");

                }

            }

            else if (Request.IsAuthenticated)

            {//page3:资源页面,为登录成功的用户提供个性化服务,比如留言

 

                if (Application[User.Identity.Name] != null)

                {

                    //调出用户的留言信息

                    this.lb_UserName.Text = Request.QueryString["username"] + "&nbsp;已登录";

                    this.tb_Messages.Text = Application[User.Identity.Name].ToString();

                }

            }

        }

    }

    protected void btn_Submit_Click(object sender, EventArgs e)

    {

        if (Request.IsAuthenticated)

        {//如果用户已登陆,可以编辑或修改留言

            if (Application[User.Identity.Name] != null)

            {//保存用户的留言信息

                Application[User.Identity.Name] = this.tb_Messages.Text;

            }

        }

        else

        {

            Response.Write("<div style='color:red;'>用户未登录不能留言!</div>");

        }

    }

</script>

<html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>XSSO子系统演示-chinaxiaofei.cnblogs.com-XSSO轻磕Web3.0</title>

</head>

<body>

    <form id="form1" runat="server">

    <div style="white-space:nowrap;">

        用户:<asp:Label runat="server" ID="lb_UserName"></asp:Label><br />

        留言:<asp:TextBox runat="server" ID="tb_Messages" Width="300" Rows="6" TextMode="MultiLine"  />

        <asp:Button runat="server" ID="btn_Submit" Text="提交" OnClick="btn_Submit_Click" />

    </div>

    </form>

</body>

</html>

Search

导航

热门文章

最新文章

Powered By duduwolf's wiki 1.0

Copyright 1999-2007 duduwolf.com Some Rights Reserved.