• asp.net 真正实现完全跨域单点登录


    单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

    asp.net跨域单点登录分为:

    1、跨子域单点登录。如 blog.a.com 和 info.a.com 这2个站点同属一个主域.a.com,实现跨子域单点登录很简单,可以利用cookie,设置Domain为”.a.com'即可,这里就不再赘叙。

    2、完成跨域单点登录。如 www.a.com www.b.com 这2个站点之间实现共享一个身份验证系统,只需在一处地方登录,下面主要谈下这种方式的实现方法。

    asp.net 跨域单点登录实现原理:

    当用户第一次访问web应用系统1的时候,因为还没有登录,会被引导到认证中心进行登录;根据用户提供的登录信息,认证系统进行身份效验,如果通过效验,返回给用户一个认证的凭据;用户再访问别的web应用的时候就会将这个Token带上,作为自己认证的凭据,应用系统接受到请求之后会把Token送到认证中心进行效验,检查Token的合法性。如果通过效验,用户就可以在不用再次登录的情况下访问应用系统2和应用系统3了。所有应用系统共享一个身份认证系统。认证系统的主要功能是将用户的登录信息和用户信息库相比较,对用户进行登录认证;认证成功后,认证系统应该生成统一的认证标志,返还给用户。另外,认证系统还应该对Token进行效验,判断其有效性。 所有应用系统能够识别和提取Token信息要实现SSO的功能,让用户只登录一次,就必须让应用系统能够识别已经登录过的用户。应用系统应该能对Token进行识别和提取,通过与认证系统的通讯,能自动判断当前用户是否登录过,从而完成单点登录的功能。

    比如说,我现在有3个分站点和1个认证中心(总站)。当用户访问分站点的时候,分站点会发Token到验证中心进行验证。验证中心判断用户是否已经登录。如果未登录,则返回到验证中心登录入口进行登录,否之则返回Token验证到分站点,直接进入分站点。

    如图所示:

    上面是实现单点登录的原理图,下面介绍下如何用asp.net实现跨域单点登录:

    一、新建网站 MasterSite,作为总站认证中心。配置web.config,采用form登录验证。
    配置如下:

    <authentication mode=”Forms”> 
    <forms name=”.AspxFormAuth” loginUrl=”Default.aspx” defaultUrl=”center.html” protection=”All” path=”/” timeout=”120”> 
    </forms> 
    </authentication> 
    <authorization> 
    <!--拒绝所有匿名用户--> 
    <deny users=”?”/> 
    </authorization> 
    <authentication mode=”Forms”>
    <forms name=”.AspxFormAuth” loginUrl=”Default.aspx” defaultUrl=”center.html” protection=”All” path=”/” timeout=”120”>
    </forms>
    </authentication>
    <authorization>
    <!--拒绝所有匿名用户-->
    <deny users=”?”/>
    </authorization>

    添加Default.aspx页面,用来进行登录。代码如下:

    HTML Code:

    <%@ Page Language=”C#” AutoEventWireup=”true” CodeFile=”Default.aspx.cs” Inherits=”_Default” %> 

    <!DOCTYPE html PUBLIC ”-//W3C//DTD XHTML 1.0 Transitional//EN” ”http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> 

    <html xmlns=”http://www.w3.org/1999/xhtml” > 
    <head runat=”server”> 
    <title>总站登录</title> 
    </head> 
    <body> 
    <form id=”form1” runat=”server”> 
    <div> 
    <asp:Login ID=”Login1” runat=”server” OnAuthenticate=”Login1_Authenticate” UserName=”test”> 
    </asp:Login> 
    </div> 
    </form> 
    </body> 
    </html> 
    <%@ Page Language=”C#” AutoEventWireup=”true” CodeFile=”Default.aspx.cs” Inherits=”_Default” %>

    <!DOCTYPE html PUBLIC ”-//W3C//DTD XHTML 1.0 Transitional//EN” ”http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>

    <html xmlns=”http://www.w3.org/1999/xhtml” >
    <head runat=”server”>
    <title>总站登录</title>
    </head>
    <body>
    <form id=”form1” runat=”server”>
    <div>
    <asp:Login ID=”Login1” runat=”server” OnAuthenticate=”Login1_Authenticate” UserName=”test”>
    </asp:Login>
    </div>
    </form>
    </body>
    </html>


    Default.cs Code:


    using System; 
    using System.Data; 
    using System.Configuration; 
    using System.Web; 
    using System.Web.Security; 
    using System.Web.UI; 
    using System.Web.UI.WebControls; 
    using System.Web.UI.WebControls.WebParts; 
    using System.Web.UI.HtmlControls; 
    using System.Text; 

    public partial class _Default : System.Web.UI.Page 

    protected void Page_Load(object sender, EventArgs e) 

    if (!IsPostBack) 

    SSORequest ssoRequest = new SSORequest(); 


    #region 验证 Post 过来的参数 
    //-------------------------------- 
    // 请求注销 
    if (!string.IsNullOrEmpty(Request[”Logout”])) 

    Authentication.Logout(); 
    return; 

    //-------------------------------- 
    // 各独立站点标识 
    if (string.IsNullOrEmpty(Request[”IASID”])) 

    return; 

    else 

    ssoRequest.IASID = Request[”IASID”]; 


    //-------------------------------- 
    // 时间戳 
    if (string.IsNullOrEmpty(Request[”TimeStamp”])) 

    return; 

    else 

    ssoRequest.TimeStamp = Request[”TimeStamp”]; 


    //-------------------------------- 
    // 各独立站点的访问地址 
    if (string.IsNullOrEmpty(Request[”AppUrl”])) 

    return; 

    else 

    ssoRequest.AppUrl = Request[”AppUrl”]; 


    //-------------------------------- 
    // 各独立站点的 Token 
    if (string.IsNullOrEmpty(Request[”Authenticator”])) 

    return; 

    else 

    ssoRequest.Authenticator = Request[”Authenticator”]; 


    ViewState[”SSORequest”] = ssoRequest; 

    #endregion 


    //验证从分站发过来的Token 
    if (Authentication.ValidateAppToken(ssoRequest)) 

    string userAccount = null; 

    // 验证用户之前是否登录过 
    //验证 EAC 认证中心的 Cookie,验证通过时获取用户登录账号 
    if (Authentication.ValidateEACCookie(out userAccount)) 

    ssoRequest.UserAccount = userAccount; 

    //创建认证中心发往各分站的 Token 
    if (Authentication.CreateEACToken(ssoRequest)) 

    Post(ssoRequest); 


    else 

    return; 


    else 

    return; 





    //post请求 
    void Post(SSORequest ssoRequest) 

    PostService ps = new PostService(); 

    ps.Url = ssoRequest.AppUrl; 

    ps.Add(”UserAccount”, ssoRequest.UserAccount); 
    ps.Add(”IASID”, ssoRequest.IASID); 
    ps.Add(”TimeStamp”, ssoRequest.TimeStamp); 
    ps.Add(”AppUrl”, ssoRequest.AppUrl); 
    ps.Add(”Authenticator”, ssoRequest.Authenticator); 

    ps.Post(); 


    /// <summary> 
    /// 验证登录账号和密码是否正确 
    /// </summary> 
    /// <param name=”userName”>登录账号</param> 
    /// <param name=”userPwd”>登录密码</param> 
    /// <returns></returns> 
    private bool ValidateUserInfo(string userName, string userPwd) 

    //从数据库中读取,验证登录账号和密码 
    //略... 
    return true; 


    protected void Login1_Authenticate(object sender, AuthenticateEventArgs e) 

    if (string.IsNullOrEmpty(Login1.UserName) || string.IsNullOrEmpty(Login1.Password)) 

    Page.RegisterClientScriptBlock(”Add”, ”<mce:script lanuage=”javascript”><!-- 
    alert('用户名密码不能为空!'); 
    // --></mce:script>”); 
    return; 

    else if (ValidateUserInfo(Login1.UserName, Login1.Password) == false) 

    Page.RegisterClientScriptBlock(”Add”, ”<mce:script lanuage=”javascript”><!-- 
    alert('用户名密码错误!'); 
    // --></mce:script>”); 
    return; 

    else 

    Session[”CurrUserName”] = Login1.UserName; 
    Session.Timeout = 120; 

    SSORequest ssoRequest = ViewState[”SSORequest”] as SSORequest; 

    // 如果不是从各分站 Post 过来的请求,则默认登录主站 
    if (ssoRequest == null) 

    FormsAuthentication.SetAuthCookie(Login1.UserName, false); 

    ssoRequest = new SSORequest(); 
    //主站标识ID 
    ssoRequest.IASID = ”00”; 
    ssoRequest.AppUrl = ”SiteList.aspx”; 
    ssoRequest.TimeStamp = DateTime.Now.ToString(”yyyy-MM-dd HH:mm”); 
    ssoRequest.Authenticator = string.Empty; 

    Response.Redirect(”SiteList.aspx”); 

    ssoRequest.UserAccount = Login1.UserName; 

    //创建Token 
    if (Authentication.CreateEACToken(ssoRequest)) 

    string expireTime = DateTime.Now.AddHours(3).ToString(”yyyy-MM-dd HH:mm”); 

    Authentication.CreatEACCookie(ssoRequest.UserAccount, ssoRequest.TimeStamp, expireTime); 

    Post(ssoRequest); 







    using System;
    using System.Data;
    using System.Configuration;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;
    using System.Text;

    public partial class _Default : System.Web.UI.Page 
    {
    protected void Page_Load(object sender, EventArgs e)
    {
    if (!IsPostBack)
    {
    SSORequest ssoRequest = new SSORequest();


    #region 验证 Post 过来的参数
    //--------------------------------
    // 请求注销
    if (!string.IsNullOrEmpty(Request[”Logout”]))
    {
    Authentication.Logout();
    return;
    }
    //--------------------------------
    // 各独立站点标识
    if (string.IsNullOrEmpty(Request[”IASID”]))
    {
    return;
    }
    else
    {
    ssoRequest.IASID = Request[”IASID”];
    }

    //--------------------------------
    // 时间戳
    if (string.IsNullOrEmpty(Request[”TimeStamp”]))
    {
    return;
    }
    else
    {
    ssoRequest.TimeStamp = Request[”TimeStamp”];
    }

    //--------------------------------
    // 各独立站点的访问地址
    if (string.IsNullOrEmpty(Request[”AppUrl”]))
    {
    return;
    }
    else
    {
    ssoRequest.AppUrl = Request[”AppUrl”];
    }

    //--------------------------------
    // 各独立站点的 Token
    if (string.IsNullOrEmpty(Request[”Authenticator”]))
    {
    return;
    }
    else
    {
    ssoRequest.Authenticator = Request[”Authenticator”];
    }

    ViewState[”SSORequest”] = ssoRequest;

    #endregion


    //验证从分站发过来的Token
    if (Authentication.ValidateAppToken(ssoRequest))
    {
    string userAccount = null;

    // 验证用户之前是否登录过
    //验证 EAC 认证中心的 Cookie,验证通过时获取用户登录账号
    if (Authentication.ValidateEACCookie(out userAccount))
    {
    ssoRequest.UserAccount = userAccount;

    //创建认证中心发往各分站的 Token
    if (Authentication.CreateEACToken(ssoRequest))
    {
    Post(ssoRequest);
    }
    }
    else
    {
    return;
    }
    }
    else
    {
    return;
    }
    }
    }


    //post请求
    void Post(SSORequest ssoRequest)
    {
    PostService ps = new PostService();

    ps.Url = ssoRequest.AppUrl;

    ps.Add(”UserAccount”, ssoRequest.UserAccount);
    ps.Add(”IASID”, ssoRequest.IASID);
    ps.Add(”TimeStamp”, ssoRequest.TimeStamp);
    ps.Add(”AppUrl”, ssoRequest.AppUrl);
    ps.Add(”Authenticator”, ssoRequest.Authenticator);

    ps.Post();
    }

    /// <summary>
    /// 验证登录账号和密码是否正确
    /// </summary>
    /// <param name=”userName”>登录账号</param>
    /// <param name=”userPwd”>登录密码</param>
    /// <returns></returns>
    private bool ValidateUserInfo(string userName, string userPwd)
    {
    //从数据库中读取,验证登录账号和密码
    //略...
    return true;
    }

    protected void Login1_Authenticate(object sender, AuthenticateEventArgs e)
    {
    if (string.IsNullOrEmpty(Login1.UserName) || string.IsNullOrEmpty(Login1.Password))
    {
    Page.RegisterClientScriptBlock(”Add”, ”<mce:script lanuage=”javascript”><!--
    alert('用户名密码不能为空!');
    // --></mce:script>”);
    return;
    }
    else if (ValidateUserInfo(Login1.UserName, Login1.Password) == false)
    {
    Page.RegisterClientScriptBlock(”Add”, ”<mce:script lanuage=”javascript”><!--
    alert('用户名密码错误!');
    // --></mce:script>”);
    return;
    }
    else
    {
    Session[”CurrUserName”] = Login1.UserName;
    Session.Timeout = 120;

    SSORequest ssoRequest = ViewState[”SSORequest”] as SSORequest;

    // 如果不是从各分站 Post 过来的请求,则默认登录主站
    if (ssoRequest == null)
    {
    FormsAuthentication.SetAuthCookie(Login1.UserName, false);

    ssoRequest = new SSORequest();
    //主站标识ID
    ssoRequest.IASID = ”00”;
    ssoRequest.AppUrl = ”SiteList.aspx”;
    ssoRequest.TimeStamp = DateTime.Now.ToString(”yyyy-MM-dd HH:mm”);
    ssoRequest.Authenticator = string.Empty;

    Response.Redirect(”SiteList.aspx”);
    }
    ssoRequest.UserAccount = Login1.UserName;

    //创建Token
    if (Authentication.CreateEACToken(ssoRequest))
    {
    string expireTime = DateTime.Now.AddHours(3).ToString(”yyyy-MM-dd HH:mm”);

    Authentication.CreatEACCookie(ssoRequest.UserAccount, ssoRequest.TimeStamp, expireTime);

    Post(ssoRequest);
    }

    }
    }


    }


    代码说明:验证分站post过来的Token请求,如果用户已经登录,则创建认证中心发往各分站的 Token验证,转向分站,否之则返回登录。若是直接登录主站则转向站点选择页面sitelist.aspx,选择你要登录的分站点。

    如图:


    二、新建站点1,代码如下:

    HTML Code:

    <%@ Page Language=”C#” AutoEventWireup=”true” CodeFile=”Default.aspx.cs” Inherits=”_Default” %> 

    <!DOCTYPE html PUBLIC ”-//W3C//DTD XHTML 1.0 Transitional//EN” ”http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> 

    <html xmlns=”http://www.w3.org/1999/xhtml” > 
    <head runat=”server”> 
    <title> 站点一</title> 
    </head> 
    <body> 
    <form id=”form1” runat=”server”> 
    <div> 
    <br /> 
    <br /> 
    <asp:LinkButton ID=”LinkButton1” runat=”server” OnClick=”LinkButton1_Click”>返回主站</asp:LinkButton> 

    <asp:LinkButton ID=”LinkButton2” runat=”server” OnClick=”LinkButton2_Click”>注销登录</asp:LinkButton></div> 
    </form> 
    </body> 
    </html> 
    <%@ Page Language=”C#” AutoEventWireup=”true” CodeFile=”Default.aspx.cs” Inherits=”_Default” %>

    <!DOCTYPE html PUBLIC ”-//W3C//DTD XHTML 1.0 Transitional//EN” ”http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>

    <html xmlns=”http://www.w3.org/1999/xhtml” >
    <head runat=”server”>
    <title> 站点一</title>
    </head>
    <body>
    <form id=”form1” runat=”server”>
    <div>
    <br />
    <br />
    <asp:LinkButton ID=”LinkButton1” runat=”server” OnClick=”LinkButton1_Click”>返回主站</asp:LinkButton>

    <asp:LinkButton ID=”LinkButton2” runat=”server” OnClick=”LinkButton2_Click”>注销登录</asp:LinkButton></div>
    </form>
    </body>
    </html>

    Default.cs code:

    using System; 
    using System.Data; 
    using System.Configuration; 
    using System.Web; 
    using System.Web.Security; 
    using System.Web.UI; 
    using System.Web.UI.WebControls; 
    using System.Web.UI.WebControls.WebParts; 
    using System.Web.UI.HtmlControls; 
    using System.Text; 

    public partial class _Default : System.Web.UI.Page 

    protected void Page_Load(object sender, EventArgs e) 

    if (!IsPostBack) 

    #region SSO 部分代码 
    SSORequest ssoRequest = new SSORequest(); 

    if (string.IsNullOrEmpty(Request[”IASID”])) 

    ssoRequest.IASID = ”01”; 
    ssoRequest.TimeStamp = DateTime.Now.ToString(”yyyy-MM-dd HH:mm”); 
    ssoRequest.AppUrl = Request.Url.ToString(); 
    Authentication.CreateAppToken(ssoRequest); 

    Post(ssoRequest); 

    else if (!string.IsNullOrEmpty(Request[”IASID”]) 
    && !string.IsNullOrEmpty(Request[”TimeStamp”]) 
    && !string.IsNullOrEmpty(Request[”AppUrl”]) 
    && !string.IsNullOrEmpty(Request[”UserAccount”]) 
    && !string.IsNullOrEmpty(Request[”Authenticator”])) 

    ssoRequest.IASID = Request[”IASID”]; 
    ssoRequest.TimeStamp = Request[”TimeStamp”]; 
    ssoRequest.AppUrl = Request[”AppUrl”]; 
    ssoRequest.UserAccount = Request[”UserAccount”]; 
    ssoRequest.Authenticator = Request[”Authenticator”]; 

    if (Authentication.ValidateEACToken(ssoRequest)) 

    //从数据库中获取UserId 
    Session[”CurrUserName”] = Request[”UserAccount”]; 
    Session.Timeout = 120; 
    FormsAuthentication.SetAuthCookie(Request[”UserAccount”], false); 
    Response.Write(string.Format(”{0},您好!欢迎来到site1, >> 访问<a href=”” mce_href=”””http://localhost/Site2/Default.aspx”>site2</a>”,ssoRequest.UserAccount)); 



    ViewState[”SSORequest”] = ssoRequest; 

    #endregion 



    void Post(SSORequest ssoRequest) 

    PostService ps = new PostService(); 
    //认证中心(主站)地址 
    string EACUrl = ”http://localhost/MasterSite/Default.aspx”; 
    ps.Url = EACUrl; 
    //ps.Add(”UserAccount”, ssoRequest.UserAccount); 
    ps.Add(”IASID”, ssoRequest.IASID); 
    ps.Add(”TimeStamp”, ssoRequest.TimeStamp); 
    ps.Add(”AppUrl”, ssoRequest.AppUrl); 
    ps.Add(”Authenticator”, ssoRequest.Authenticator); 

    ps.Post(); 



    //注销登录 
    protected void LinkButton2_Click(object sender, EventArgs e) 

    FormsAuthentication.SignOut(); 

    SSORequest ssoRequest = new SSORequest(); 

    ssoRequest.IASID = ”01”; 
    ssoRequest.TimeStamp = DateTime.Now.ToString(”yyyy-MM-dd HH:mm”); 
    ssoRequest.AppUrl = Request.Url.ToString(); 

    Authentication.CreateAppToken(ssoRequest); 

    PostService ps = new PostService(); 

    //认证中心(主站)地址 
    string EACUrl = ”http://localhost/MasterSite/Default.aspx”; 
    ps.Url = EACUrl; 

    ps.Add(”IASID”, ssoRequest.IASID); 
    ps.Add(”TimeStamp”, ssoRequest.TimeStamp); 
    ps.Add(”AppUrl”, ssoRequest.AppUrl); 
    ps.Add(”Authenticator”, ssoRequest.Authenticator); 

    ps.Add(”Logout”, ”true”); 

    ps.Post(); 


    //返回主站 
    protected void LinkButton1_Click(object sender, EventArgs e) 

    if (Session[”CurrUserName”] != null) 

    Response.Redirect(”http://localhost/MasterSite/SiteList.aspx”); 



    using System;
    using System.Data;
    using System.Configuration;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;
    using System.Text;

    public partial class _Default : System.Web.UI.Page 
    {
    protected void Page_Load(object sender, EventArgs e)
    {
    if (!IsPostBack)
    {
    #region SSO 部分代码
    SSORequest ssoRequest = new SSORequest();

    if (string.IsNullOrEmpty(Request[”IASID”]))
    {
    ssoRequest.IASID = ”01”;
    ssoRequest.TimeStamp = DateTime.Now.ToString(”yyyy-MM-dd HH:mm”);
    ssoRequest.AppUrl = Request.Url.ToString();
    Authentication.CreateAppToken(ssoRequest);

    Post(ssoRequest);
    }
    else if (!string.IsNullOrEmpty(Request[”IASID”])
    && !string.IsNullOrEmpty(Request[”TimeStamp”])
    && !string.IsNullOrEmpty(Request[”AppUrl”])
    && !string.IsNullOrEmpty(Request[”UserAccount”])
    && !string.IsNullOrEmpty(Request[”Authenticator”]))
    {
    ssoRequest.IASID = Request[”IASID”];
    ssoRequest.TimeStamp = Request[”TimeStamp”];
    ssoRequest.AppUrl = Request[”AppUrl”];
    ssoRequest.UserAccount = Request[”UserAccount”];
    ssoRequest.Authenticator = Request[”Authenticator”];

    if (Authentication.ValidateEACToken(ssoRequest))
    {
    //从数据库中获取UserId
    Session[”CurrUserName”] = Request[”UserAccount”];
    Session.Timeout = 120;
    FormsAuthentication.SetAuthCookie(Request[”UserAccount”], false);
    Response.Write(string.Format(”{0},您好!欢迎来到site1, >> 访问<a href=”” mce_href=”””http://localhost/Site2/Default.aspx”>site2</a>”,ssoRequest.UserAccount));
    }
    }

    ViewState[”SSORequest”] = ssoRequest;

    #endregion
    }
    }

    void Post(SSORequest ssoRequest)
    {
    PostService ps = new PostService();
    //认证中心(主站)地址
    string EACUrl = ”http://localhost/MasterSite/Default.aspx”;
    ps.Url = EACUrl;
    //ps.Add(”UserAccount”, ssoRequest.UserAccount);
    ps.Add(”IASID”, ssoRequest.IASID);
    ps.Add(”TimeStamp”, ssoRequest.TimeStamp);
    ps.Add(”AppUrl”, ssoRequest.AppUrl);
    ps.Add(”Authenticator”, ssoRequest.Authenticator);

    ps.Post();
    }


    //注销登录
    protected void LinkButton2_Click(object sender, EventArgs e)
    {
    FormsAuthentication.SignOut();

    SSORequest ssoRequest = new SSORequest();

    ssoRequest.IASID = ”01”;
    ssoRequest.TimeStamp = DateTime.Now.ToString(”yyyy-MM-dd HH:mm”);
    ssoRequest.AppUrl = Request.Url.ToString();

    Authentication.CreateAppToken(ssoRequest);

    PostService ps = new PostService();

    //认证中心(主站)地址
    string EACUrl = ”http://localhost/MasterSite/Default.aspx”;
    ps.Url = EACUrl;

    ps.Add(”IASID”, ssoRequest.IASID);
    ps.Add(”TimeStamp”, ssoRequest.TimeStamp);
    ps.Add(”AppUrl”, ssoRequest.AppUrl);
    ps.Add(”Authenticator”, ssoRequest.Authenticator);

    ps.Add(”Logout”, ”true”);

    ps.Post();
    }

    //返回主站
    protected void LinkButton1_Click(object sender, EventArgs e)
    {
    if (Session[”CurrUserName”] != null)
    {
    Response.Redirect(”http://localhost/MasterSite/SiteList.aspx”);
    }
    }
    }

    配置web.config

    <authentication mode=”Forms”> 
    <forms name=”.AspxFormAuth” loginUrl=”Default.aspx” defaultUrl=”center.html” protection=”All” path=”/” timeout=”60”> 
    </forms> 
    </authentication> 
    <authorization> 
    <!--拒绝所有匿名用户--> 
    <deny users=”?”/> 
    </authorization> 
    <authentication mode=”Forms”>
    <forms name=”.AspxFormAuth” loginUrl=”Default.aspx” defaultUrl=”center.html” protection=”All” path=”/” timeout=”60”>
    </forms>
    </authentication>
    <authorization>
    <!--拒绝所有匿名用户-->
    <deny users=”?”/>
    </authorization>

    三、同二一样,新建站点Site2,代码如下:

    using System; 
    using System.Data; 
    using System.Configuration; 
    using System.Web; 
    using System.Web.Security; 
    using System.Web.UI; 
    using System.Web.UI.WebControls; 
    using System.Web.UI.WebControls.WebParts; 
    using System.Web.UI.HtmlControls; 

    public partial class _Default : System.Web.UI.Page 

    protected void Page_Load(object sender, EventArgs e) 

    if (!IsPostBack) 

    #region SSO 部分代码 
    SSORequest ssoRequest = new SSORequest(); 

    if (string.IsNullOrEmpty(Request[”IASID”])) 

    ssoRequest.IASID = ”02”; 
    ssoRequest.TimeStamp = DateTime.Now.ToString(”yyyy-MM-dd HH:mm”); 
    ssoRequest.AppUrl = Request.Url.ToString(); 
    Authentication.CreateAppToken(ssoRequest); 

    Post(ssoRequest); 

    else if (!string.IsNullOrEmpty(Request[”IASID”]) 
    && !string.IsNullOrEmpty(Request[”TimeStamp”]) 
    && !string.IsNullOrEmpty(Request[”AppUrl”]) 
    && !string.IsNullOrEmpty(Request[”UserAccount”]) 
    && !string.IsNullOrEmpty(Request[”Authenticator”])) 

    ssoRequest.IASID = Request[”IASID”]; 
    ssoRequest.TimeStamp = Request[”TimeStamp”]; 
    ssoRequest.AppUrl = Request[”AppUrl”]; 
    ssoRequest.UserAccount = Request[”UserAccount”]; 
    ssoRequest.Authenticator = Request[”Authenticator”]; 

    if (Authentication.ValidateEACToken(ssoRequest)) 

    Session[”CurrUserName”] = Request[”UserAccount”]; 
    Session.Timeout = 120; 
    FormsAuthentication.SetAuthCookie(Request[”UserAccount”], false); 
    Response.Write(string.Format(”{0},您好!欢迎来到site2, >> 访问<a href=”” mce_href=”””http://localhost/Site1/Default.aspx”>site1</a>”, ssoRequest.UserAccount)); 



    ViewState[”SSORequest”] = ssoRequest; 

    #endregion 



    void Post(SSORequest ssoRequest) 

    PostService ps = new PostService(); 
    //认证中心(主站)地址 
    string EACUrl = ”http://localhost/MasterSite/Default.aspx”; 
    ps.Url = EACUrl; 
    //ps.Add(”UserAccount”, ssoRequest.UserAccount); 
    ps.Add(”IASID”, ssoRequest.IASID); 
    ps.Add(”TimeStamp”, ssoRequest.TimeStamp); 
    ps.Add(”AppUrl”, ssoRequest.AppUrl); 
    ps.Add(”Authenticator”, ssoRequest.Authenticator); 

    ps.Post(); 



    //注销登录 
    protected void LinkButton2_Click(object sender, EventArgs e) 

    FormsAuthentication.SignOut(); 

    SSORequest ssoRequest = new SSORequest(); 

    ssoRequest.IASID = ”02”; 
    ssoRequest.TimeStamp = DateTime.Now.ToString(”yyyy-MM-dd HH:mm”); 
    ssoRequest.AppUrl = Request.Url.ToString(); 

    Authentication.CreateAppToken(ssoRequest); 

    PostService ps = new PostService(); 

    //认证中心(主站)地址 
    string EACUrl = ”http://localhost/MasterSite/Default.aspx”; 
    ps.Url = EACUrl; 

    ps.Add(”IASID”, ssoRequest.IASID); 
    ps.Add(”TimeStamp”, ssoRequest.TimeStamp); 
    ps.Add(”AppUrl”, ssoRequest.AppUrl); 
    ps.Add(”Authenticator”, ssoRequest.Authenticator); 

    ps.Add(”Logout”, ”true”); 

    ps.Post(); 


    //返回主站 
    protected void LinkButton1_Click(object sender, EventArgs e) 

    if (Session[”CurrUserName”] != null) 

    Response.Redirect(”http://localhost/MasterSite/SiteList.aspx”); 



    using System;
    using System.Data;
    using System.Configuration;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;

    public partial class _Default : System.Web.UI.Page
    {
    protected void Page_Load(object sender, EventArgs e)
    {
    if (!IsPostBack)
    {
    #region SSO 部分代码
    SSORequest ssoRequest = new SSORequest();

    if (string.IsNullOrEmpty(Request[”IASID”]))
    {
    ssoRequest.IASID = ”02”;
    ssoRequest.TimeStamp = DateTime.Now.ToString(”yyyy-MM-dd HH:mm”);
    ssoRequest.AppUrl = Request.Url.ToString();
    Authentication.CreateAppToken(ssoRequest);

    Post(ssoRequest);
    }
    else if (!string.IsNullOrEmpty(Request[”IASID”])
    && !string.IsNullOrEmpty(Request[”TimeStamp”])
    && !string.IsNullOrEmpty(Request[”AppUrl”])
    && !string.IsNullOrEmpty(Request[”UserAccount”])
    && !string.IsNullOrEmpty(Request[”Authenticator”]))
    {
    ssoRequest.IASID = Request[”IASID”];
    ssoRequest.TimeStamp = Request[”TimeStamp”];
    ssoRequest.AppUrl = Request[”AppUrl”];
    ssoRequest.UserAccount = Request[”UserAccount”];
    ssoRequest.Authenticator = Request[”Authenticator”];

    if (Authentication.ValidateEACToken(ssoRequest))
    {
    Session[”CurrUserName”] = Request[”UserAccount”];
    Session.Timeout = 120;
    FormsAuthentication.SetAuthCookie(Request[”UserAccount”], false);
    Response.Write(string.Format(”{0},您好!欢迎来到site2, >> 访问<a href=”” mce_href=”””http://localhost/Site1/Default.aspx”>site1</a>”, ssoRequest.UserAccount));
    }
    }

    ViewState[”SSORequest”] = ssoRequest;

    #endregion
    }
    }

    void Post(SSORequest ssoRequest)
    {
    PostService ps = new PostService();
    //认证中心(主站)地址
    string EACUrl = ”http://localhost/MasterSite/Default.aspx”;
    ps.Url = EACUrl;
    //ps.Add(”UserAccount”, ssoRequest.UserAccount);
    ps.Add(”IASID”, ssoRequest.IASID);
    ps.Add(”TimeStamp”, ssoRequest.TimeStamp);
    ps.Add(”AppUrl”, ssoRequest.AppUrl);
    ps.Add(”Authenticator”, ssoRequest.Authenticator);

    ps.Post();
    }


    //注销登录
    protected void LinkButton2_Click(object sender, EventArgs e)
    {
    FormsAuthentication.SignOut();

    SSORequest ssoRequest = new SSORequest();

    ssoRequest.IASID = ”02”;
    ssoRequest.TimeStamp = DateTime.Now.ToString(”yyyy-MM-dd HH:mm”);
    ssoRequest.AppUrl = Request.Url.ToString();

    Authentication.CreateAppToken(ssoRequest);

    PostService ps = new PostService();

    //认证中心(主站)地址
    string EACUrl = ”http://localhost/MasterSite/Default.aspx”;
    ps.Url = EACUrl;

    ps.Add(”IASID”, ssoRequest.IASID);
    ps.Add(”TimeStamp”, ssoRequest.TimeStamp);
    ps.Add(”AppUrl”, ssoRequest.AppUrl);
    ps.Add(”Authenticator”, ssoRequest.Authenticator);

    ps.Add(”Logout”, ”true”);

    ps.Post();
    }

    //返回主站
    protected void LinkButton1_Click(object sender, EventArgs e)
    {
    if (Session[”CurrUserName”] != null)
    {
    Response.Redirect(”http://localhost/MasterSite/SiteList.aspx”);
    }
    }
    }

    对于tokent请求,tokent验证,需要对它进行加密、解密。


    其它代码:

    Authentication.cs
    using System; 
    using System.Data; 
    using System.Configuration; 
    using System.Web; 
    using System.Web.Security; 
    using System.Collections.Generic; 
    using System.Text; 

    /// <summary> 
    /// 安全验证类 
    /// </summary> 
    public class Authentication 

    static readonly string cookieName = ”EACToken”; 
    static readonly string hashSplitter = ”|”; 

    public Authentication() 



    public static string GetAppKey(int appID) 

    //string cmdText = @”select * from ”; 
    return string.Empty; 


    public static string GetAppKey() 

    return ”22362E7A9285DD53A0BBC2932F9733C505DC04EDBFE00D70”; 


    public static string GetAppIV() 

    return ”1E7FA9231E7FA923”; 


    /// <summary> 
    /// 取得加密服务 
    /// </summary> 
    /// <returns></returns> 
    static CryptoService GetCryptoService() 

    string key = GetAppKey(); 
    string IV = GetAppIV(); 

    CryptoService cs = new CryptoService(key, IV); 
    return cs; 


    /// <summary> 
    /// 创建各分站发往认证中心的 Token 
    /// </summary> 
    /// <param name=”ssoRequest”></param> 
    /// <returns></returns> 
    public static bool CreateAppToken(SSORequest ssoRequest) 

    string OriginalAuthenticator = ssoRequest.IASID + ssoRequest.TimeStamp + ssoRequest.AppUrl; 
    string AuthenticatorDigest = CryptoHelper.ComputeHashString(OriginalAuthenticator);
    string sToEncrypt = OriginalAuthenticator + AuthenticatorDigest; 
    byte[] bToEncrypt = CryptoHelper.ConvertStringToByteArray(sToEncrypt); 

    CryptoService cs = GetCryptoService(); 

    byte[] encrypted; 

    if (cs.Encrypt(bToEncrypt, out encrypted)) 

    ssoRequest.Authenticator = CryptoHelper.ToBase64String(encrypted); 

    return true; 

    else 

    return false; 




    /// <summary> 
    /// 验证从各分站发送过来的 Token 
    /// </summary> 
    /// <param name=”ssoRequest”></param> 
    /// <returns></returns> 
    public static bool ValidateAppToken(SSORequest ssoRequest) 

    string Authenticator = ssoRequest.Authenticator; 

    string OriginalAuthenticator = ssoRequest.IASID + ssoRequest.TimeStamp + ssoRequest.AppUrl; 
    string AuthenticatorDigest = CryptoHelper.ComputeHashString(OriginalAuthenticator);
    string sToEncrypt = OriginalAuthenticator + AuthenticatorDigest; 
    byte[] bToEncrypt = CryptoHelper.ConvertStringToByteArray(sToEncrypt); 

    CryptoService cs = GetCryptoService(); 
    byte[] encrypted; 

    if (cs.Encrypt(bToEncrypt, out encrypted)) 

    return Authenticator == CryptoHelper.ToBase64String(encrypted); 

    else 

    return false; 




    /// <summary> 
    /// 创建认证中心发往各分站的 Token 
    /// </summary> 
    /// <param name=”ssoRequest”></param> 
    /// <returns></returns> 
    public static bool CreateEACToken(SSORequest ssoRequest) 

    string OriginalAuthenticator = ssoRequest.UserAccount + ssoRequest.IASID + ssoRequest.TimeStamp + ssoRequest.AppUrl; 
    string AuthenticatorDigest = CryptoHelper.ComputeHashString(OriginalAuthenticator);
    string sToEncrypt = OriginalAuthenticator + AuthenticatorDigest; 
    byte[] bToEncrypt = CryptoHelper.ConvertStringToByteArray(sToEncrypt); 

    CryptoService cs = GetCryptoService(); 
    byte[] encrypted; 

    if (cs.Encrypt(bToEncrypt, out encrypted)) 

    ssoRequest.Authenticator = CryptoHelper.ToBase64String(encrypted); 

    return true; 

    else 

    return false; 




    /// <summary> 
    /// 验证从认证中心发送过来的 Token 
    /// </summary> 
    /// <param name=”ssoRequest”></param> 
    /// <returns></returns> 
    public static bool ValidateEACToken(SSORequest ssoRequest) 

    string Authenticator = ssoRequest.Authenticator; 

    string OriginalAuthenticator = ssoRequest.UserAccount + ssoRequest.IASID + ssoRequest.TimeStamp + ssoRequest.AppUrl; 
    string AuthenticatorDigest = CryptoHelper.ComputeHashString(OriginalAuthenticator);
    string sToEncrypt = OriginalAuthenticator + AuthenticatorDigest; 
    byte[] bToEncrypt = CryptoHelper.ConvertStringToByteArray(sToEncrypt); 

    string EncryCurrentAuthenticator = string.Empty; 
    CryptoService cs = GetCryptoService(); 
    byte[] encrypted; 

    if (cs.Encrypt(bToEncrypt, out encrypted)) 

    EncryCurrentAuthenticator = CryptoHelper.ToBase64String(encrypted); 

    return Authenticator == EncryCurrentAuthenticator; 

    else 

    return false; 




    /// <summary> 
    /// 创建 EAC 认证中心的 Cookie 
    /// </summary> 
    /// <param name=”userAccount”></param> 
    /// <param name=”timeStamp”></param> 
    /// <param name=”expireTime”></param> 
    /// <param name=”cookieValue”></param> 
    /// <returns></returns> 
    public static bool CreatEACCookie(string userAccount, string timeStamp, string expireTime) 

    string plainText = ”UserAccount=” + userAccount + ”;TimeStamp=” + timeStamp + ”;ExpireTime=” + expireTime; 
    plainText += hashSplitter + CryptoHelper.ComputeHashString(plainText); 

    CryptoService cs = GetCryptoService(); 
    byte[] encrypted; 

    if (cs.Encrypt(CryptoHelper.ConvertStringToByteArray(plainText), out encrypted)) 

    string cookieValue = CryptoHelper.ToBase64String(encrypted); 
    SetCookie(cookieValue); 

    return true; 

    else 

    return false; 



    /// <summary> 
    /// 验证 EAC 认证中心的 Cookie,验证通过时获取用户登录账号 
    /// </summary> 
    /// <param name=”userAccount”>输出用户登录账号</param> 
    /// <returns></returns> 
    public static bool ValidateEACCookie(out string userAccount) 

    userAccount = string.Empty; 
    try 


    string cookieValue = GetCookie().Value; 
    byte[] toDecrypt = CryptoHelper.FromBase64String(cookieValue); 
    CryptoService cs = GetCryptoService(); 

    string decrypted = string.Empty; 
    if (cs.Decrypt(toDecrypt, out decrypted)) 


    string[] arrTemp = decrypted.Split(Convert.ToChar(hashSplitter)); 
    string plainText = arrTemp[0]; 
    string hashedText = arrTemp[1]; 

    userAccount = plainText.Split(Convert.ToChar(”;”))[0].Split(Convert.ToChar(”=”))[1]; 

    return hashedText.Replace(””, string.Empty) == CryptoHelper.ComputeHashString(plainText); 


    else 

    return false; 


    catch (Exception e) 

    return false; 




    public static void Logout() 

    HttpContext.Current.Response.Cookies[cookieName].Expires = DateTime.Parse(”1900-1-1”); 
    HttpContext.Current.Response.Cookies[cookieName].Path = ”/”; 


    private static void SetCookie(string cookieValue) 

    HttpContext.Current.Response.Cookies[cookieName].Value = cookieValue; 
    HttpContext.Current.Response.Cookies[cookieName].Expires = DateTime.Now.AddHours(24); 
    HttpContext.Current.Response.Cookies[cookieName].Path = ”/”; 


    private static HttpCookie GetCookie() 

    HttpCookie cookie = HttpContext.Current.Request.Cookies[”EACToken”]; 
    return cookie; 


    using System;
    using System.Data;
    using System.Configuration;
    using System.Web;
    using System.Web.Security;
    using System.Collections.Generic;
    using System.Text;

    /// <summary>
    /// 安全验证类
    /// </summary>
    public class Authentication
    {
    static readonly string cookieName = ”EACToken”;
    static readonly string hashSplitter = ”|”;

    public Authentication()
    {
    }

    public static string GetAppKey(int appID)
    {
    //string cmdText = @”select * from ”;
    return string.Empty;
    }

    public static string GetAppKey()
    {
    return ”22362E7A9285DD53A0BBC2932F9733C505DC04EDBFE00D70”;
    }

    public static string GetAppIV()
    {
    return ”1E7FA9231E7FA923”;
    }

    /// <summary>
    /// 取得加密服务
    /// </summary>
    /// <returns></returns>
    static CryptoService GetCryptoService()
    {
    string key = GetAppKey();
    string IV = GetAppIV();

    CryptoService cs = new CryptoService(key, IV);
    return cs;
    }

    /// <summary>
    /// 创建各分站发往认证中心的 Token
    /// </summary>
    /// <param name=”ssoRequest”></param>
    /// <returns></returns>
    public static bool CreateAppToken(SSORequest ssoRequest)
    {
    string OriginalAuthenticator = ssoRequest.IASID + ssoRequest.TimeStamp + ssoRequest.AppUrl;
    string AuthenticatorDigest = CryptoHelper.ComputeHashString(OriginalAuthenticator);
    string sToEncrypt = OriginalAuthenticator + AuthenticatorDigest;
    byte[] bToEncrypt = CryptoHelper.ConvertStringToByteArray(sToEncrypt);

    CryptoService cs = GetCryptoService();

    byte[] encrypted;

    if (cs.Encrypt(bToEncrypt, out encrypted))
    {
    ssoRequest.Authenticator = CryptoHelper.ToBase64String(encrypted);

    return true;
    }
    else
    {
    return false;
    }
    }


    /// <summary>
    /// 验证从各分站发送过来的 Token
    /// </summary>
    /// <param name=”ssoRequest”></param>
    /// <returns></returns>
    public static bool ValidateAppToken(SSORequest ssoRequest)
    {
    string Authenticator = ssoRequest.Authenticator;

    string OriginalAuthenticator = ssoRequest.IASID + ssoRequest.TimeStamp + ssoRequest.AppUrl;
    string AuthenticatorDigest = CryptoHelper.ComputeHashString(OriginalAuthenticator);
    string sToEncrypt = OriginalAuthenticator + AuthenticatorDigest;
    byte[] bToEncrypt = CryptoHelper.ConvertStringToByteArray(sToEncrypt);

    CryptoService cs = GetCryptoService();
    byte[] encrypted;

    if (cs.Encrypt(bToEncrypt, out encrypted))
    {
    return Authenticator == CryptoHelper.ToBase64String(encrypted);
    }
    else
    {
    return false;
    }
    }


    /// <summary>
    /// 创建认证中心发往各分站的 Token
    /// </summary>
    /// <param name=”ssoRequest”></param>
    /// <returns></returns>
    public static bool CreateEACToken(SSORequest ssoRequest)
    {
    string OriginalAuthenticator = ssoRequest.UserAccount + ssoRequest.IASID + ssoRequest.TimeStamp + ssoRequest.AppUrl;
    string AuthenticatorDigest = CryptoHelper.ComputeHashString(OriginalAuthenticator);
    string sToEncrypt = OriginalAuthenticator + AuthenticatorDigest;
    byte[] bToEncrypt = CryptoHelper.ConvertStringToByteArray(sToEncrypt);

    CryptoService cs = GetCryptoService();
    byte[] encrypted;

    if (cs.Encrypt(bToEncrypt, out encrypted))
    {
    ssoRequest.Authenticator = CryptoHelper.ToBase64String(encrypted);

    return true;
    }
    else
    {
    return false;
    }
    }


    /// <summary>
    /// 验证从认证中心发送过来的 Token
    /// </summary>
    /// <param name=”ssoRequest”></param>
    /// <returns></returns>
    public static bool ValidateEACToken(SSORequest ssoRequest)
    {
    string Authenticator = ssoRequest.Authenticator;

    string OriginalAuthenticator = ssoRequest.UserAccount + ssoRequest.IASID + ssoRequest.TimeStamp + ssoRequest.AppUrl;
    string AuthenticatorDigest = CryptoHelper.ComputeHashString(OriginalAuthenticator);
    string sToEncrypt = OriginalAuthenticator + AuthenticatorDigest;
    byte[] bToEncrypt = CryptoHelper.ConvertStringToByteArray(sToEncrypt);

    string EncryCurrentAuthenticator = string.Empty;
    CryptoService cs = GetCryptoService();
    byte[] encrypted;

    if (cs.Encrypt(bToEncrypt, out encrypted))
    {
    EncryCurrentAuthenticator = CryptoHelper.ToBase64String(encrypted);

    return Authenticator == EncryCurrentAuthenticator;
    }
    else
    {
    return false;
    }
    }


    /// <summary>
    /// 创建 EAC 认证中心的 Cookie
    /// </summary>
    /// <param name=”userAccount”></param>
    /// <param name=”timeStamp”></param>
    /// <param name=”expireTime”></param>
    /// <param name=”cookieValue”></param>
    /// <returns></returns>
    public static bool CreatEACCookie(string userAccount, string timeStamp, string expireTime)
    {
    string plainText = ”UserAccount=” + userAccount + ”;TimeStamp=” + timeStamp + ”;ExpireTime=” + expireTime;
    plainText += hashSplitter + CryptoHelper.ComputeHashString(plainText);

    CryptoService cs = GetCryptoService();
    byte[] encrypted;

    if (cs.Encrypt(CryptoHelper.ConvertStringToByteArray(plainText), out encrypted))
    {
    string cookieValue = CryptoHelper.ToBase64String(encrypted);
    SetCookie(cookieValue);

    return true;
    }
    else
    {
    return false;
    }
    }

    /// <summary>
    /// 验证 EAC 认证中心的 Cookie,验证通过时获取用户登录账号
    /// </summary>
    /// <param name=”userAccount”>输出用户登录账号</param>
    /// <returns></returns>
    public static bool ValidateEACCookie(out string userAccount)
    {
    userAccount = string.Empty;
    try
    {

    string cookieValue = GetCookie().Value;
    byte[] toDecrypt = CryptoHelper.FromBase64String(cookieValue);
    CryptoService cs = GetCryptoService();

    string decrypted = string.Empty;
    if (cs.Decrypt(toDecrypt, out decrypted))
    {

    string[] arrTemp = decrypted.Split(Convert.ToChar(hashSplitter));
    string plainText = arrTemp[0];
    string hashedText = arrTemp[1];

    userAccount = plainText.Split(Convert.ToChar(”;”))[0].Split(Convert.ToChar(”=”))[1];

    return hashedText.Replace(””, string.Empty) == CryptoHelper.ComputeHashString(plainText);

    }
    else
    {
    return false;
    }
    }
    catch (Exception e)
    {
    return false;
    }
    }


    public static void Logout()
    {
    HttpContext.Current.Response.Cookies[cookieName].Expires = DateTime.Parse(”1900-1-1”);
    HttpContext.Current.Response.Cookies[cookieName].Path = ”/”;
    }

    private static void SetCookie(string cookieValue)
    {
    HttpContext.Current.Response.Cookies[cookieName].Value = cookieValue;
    HttpContext.Current.Response.Cookies[cookieName].Expires = DateTime.Now.AddHours(24);
    HttpContext.Current.Response.Cookies[cookieName].Path = ”/”;
    }

    private static HttpCookie GetCookie()
    {
    HttpCookie cookie = HttpContext.Current.Request.Cookies[”EACToken”];
    return cookie;
    }
    }


    CryptoHelper.cs

    using System; 
    using System.Collections.Generic; 
    using System.Text; 
    using System.Security.Cryptography; 

    public class CryptoHelper 

    /// <summary> 
    /// 复合 Hash:string --> byte[] --> hashed byte[] --> base64 string 
    /// </summary> 
    /// <param name=”s”></param> 
    /// <returns></returns> 
    public static string ComputeHashString(string s) 

    return ToBase64String(ComputeHash(ConvertStringToByteArray(s))); 



    public static byte[] ComputeHash(byte[] buf) 

    //return ((HashAlgorithm)CryptoConfig.CreateFromName(”SHA1”)).ComputeHash(buf);
    return SHA1.Create().ComputeHash(buf); 



    /// <summary> 
    /// //System.Convert.ToBase64String 
    /// </summary> 
    /// <param name=”buf”></param> 
    /// <returns></returns> 
    public static string ToBase64String(byte[] buf) 

    return System.Convert.ToBase64String(buf); 



    public static byte[] FromBase64String(string s) 

    return System.Convert.FromBase64String(s); 


    /// <summary> 
    /// //Encoding.UTF8.GetBytes(s) 
    /// </summary> 
    /// <param name=”s”></param> 
    /// <returns></returns> 
    public static byte[] ConvertStringToByteArray(String s) 

    return Encoding.UTF8.GetBytes(s);//gb2312 



    public static string ConvertByteArrayToString(byte[] buf) 

    //return System.Text.Encoding.GetEncoding(”utf-8”).GetString(buf); 

    return Encoding.UTF8.GetString(buf); 



    /// <summary> 
    /// 字节数组转换为十六进制字符串 
    /// </summary> 
    /// <param name=”buf”></param> 
    /// <returns></returns> 
    public static string ByteArrayToHexString(byte[] buf) 

    StringBuilder sb = new StringBuilder(); 
    for (int i = 0; i < buf.Length; i++) 

    sb.Append(buf[i].ToString(”X”).Length == 2 ? buf[i].ToString(”X”) : ”0” + buf[i].ToString(”X”)); 

    return sb.ToString(); 


    /// <summary> 
    /// 十六进制字符串转换为字节数组 
    /// </summary> 
    /// <param name=”s”></param> 
    /// <returns></returns> 
    public static byte[] HexStringToByteArray(string s) 

    Byte[] buf = new byte[s.Length / 2]; 
    for (int i = 0; i < buf.Length; i++) 

    buf[i] = (byte)(Char2Hex(s.Substring(i * 2, 1)) * 0x10 + Char2Hex(s.Substring(i * 2 + 1, 1))); 

    return buf; 



    private static byte Char2Hex(string chr) 

    switch (chr) 

    case ”0”: 
    return 0x00; 
    case ”1”: 
    return 0x01; 
    case ”2”: 
    return 0x02; 
    case ”3”: 
    return 0x03; 
    case ”4”: 
    return 0x04; 
    case ”5”: 
    return 0x05; 
    case ”6”: 
    return 0x06; 
    case ”7”: 
    return 0x07; 
    case ”8”: 
    return 0x08; 
    case ”9”: 
    return 0x09; 
    case ”A”: 
    return 0x0a; 
    case ”B”: 
    return 0x0b; 
    case ”C”: 
    return 0x0c; 
    case ”D”: 
    return 0x0d; 
    case ”E”: 
    return 0x0e; 
    case ”F”: 
    return 0x0f; 

    return 0x00; 


    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Security.Cryptography;

    public class CryptoHelper
    {
    /// <summary>
    /// 复合 Hash:string --> byte[] --> hashed byte[] --> base64 string
    /// </summary>
    /// <param name=”s”></param>
    /// <returns></returns>
    public static string ComputeHashString(string s)
    {
    return ToBase64String(ComputeHash(ConvertStringToByteArray(s)));
    }


    public static byte[] ComputeHash(byte[] buf)
    {
    //return ((HashAlgorithm)CryptoConfig.CreateFromName(”SHA1”)).ComputeHash(buf);
    return SHA1.Create().ComputeHash(buf);

    }

    /// <summary>
    /// //System.Convert.ToBase64String
    /// </summary>
    /// <param name=”buf”></param>
    /// <returns></returns>
    public static string ToBase64String(byte[] buf)
    {
    return System.Convert.ToBase64String(buf);
    }


    public static byte[] FromBase64String(string s)
    {
    return System.Convert.FromBase64String(s);
    }

    /// <summary>
    /// //Encoding.UTF8.GetBytes(s)
    /// </summary>
    /// <param name=”s”></param>
    /// <returns></returns>
    public static byte[] ConvertStringToByteArray(String s)
    {
    return Encoding.UTF8.GetBytes(s);//gb2312
    }


    public static string ConvertByteArrayToString(byte[] buf)
    {
    //return System.Text.Encoding.GetEncoding(”utf-8”).GetString(buf);

    return Encoding.UTF8.GetString(buf);
    }


    /// <summary>
    /// 字节数组转换为十六进制字符串
    /// </summary>
    /// <param name=”buf”></param>
    /// <returns></returns>
    public static string ByteArrayToHexString(byte[] buf)
    {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < buf.Length; i++)
    {
    sb.Append(buf[i].ToString(”X”).Length == 2 ? buf[i].ToString(”X”) : ”0” + buf[i].ToString(”X”));
    }
    return sb.ToString();
    }

    /// <summary>
    /// 十六进制字符串转换为字节数组
    /// </summary>
    /// <param name=”s”></param>
    /// <returns></returns>
    public static byte[] HexStringToByteArray(string s)
    {
    Byte[] buf = new byte[s.Length / 2];
    for (int i = 0; i < buf.Length; i++)
    {
    buf[i] = (byte)(Char2Hex(s.Substring(i * 2, 1)) * 0x10 + Char2Hex(s.Substring(i * 2 + 1, 1)));
    }
    return buf;
    }


    private static byte Char2Hex(string chr)
    {
    switch (chr)
    {
    case ”0”:
    return 0x00;
    case ”1”:
    return 0x01;
    case ”2”:
    return 0x02;
    case ”3”:
    return 0x03;
    case ”4”:
    return 0x04;
    case ”5”:
    return 0x05;
    case ”6”:
    return 0x06;
    case ”7”:
    return 0x07;
    case ”8”:
    return 0x08;
    case ”9”:
    return 0x09;
    case ”A”:
    return 0x0a;
    case ”B”:
    return 0x0b;
    case ”C”:
    return 0x0c;
    case ”D”:
    return 0x0d;
    case ”E”:
    return 0x0e;
    case ”F”:
    return 0x0f;
    }
    return 0x00;
    }
    }


    CryptoService.cs

    using System; 
    using System.Data; 
    using System.Configuration; 
    using System.Web; 
    using System.Web.Security; 
    using System.Web.UI; 
    using System.Web.UI.WebControls; 
    using System.Web.UI.WebControls.WebParts; 
    using System.Web.UI.HtmlControls; 
    using System.Text; 
    using System.Security.Cryptography; 
    using System.IO; 

    public class CryptoService 

    /// <summary> 
    /// 加密的密钥 
    /// </summary> 
    string sKey = ”22362E7A9285DD53A0BBC2932F9733C505DC04EDBFE00D70”; 
    string sIV = ”1E7FA9231E7FA923”; 

    byte[] byteKey; 
    byte[] byteIV; 

    /// <summary> 
    /// 加密向量 
    /// </summary> 
    static byte[] bIV ={ 1, 2, 3, 4, 5, 6, 7, 8 }; 

    public CryptoService() 
    { } 

    public CryptoService(string key, string IV) 

    sKey = key; 
    sIV = IV; 

    byteKey = CryptoHelper.HexStringToByteArray(sKey); 
    byteIV = CryptoHelper.HexStringToByteArray(sIV); 




    /// <summary> 
    /// 将明文加密,返回密文 
    /// </summary> 
    /// <param name=”Data”>要加密的字串</param> 
    /// <returns></returns> 
    public byte[] Encrypt(string Data) 

    try 

    byte[] ret; 

    using (MemoryStream mStream = new MemoryStream()) 
    using (CryptoStream cStream = new CryptoStream(mStream, 
    new TripleDESCryptoServiceProvider().CreateEncryptor(byteKey, byteIV), 
    CryptoStreamMode.Write)) 


    byte[] toEncrypt = new ASCIIEncoding().GetBytes(Data); 

    // Write the byte array to the crypto stream and flush it. 
    cStream.Write(toEncrypt, 0, toEncrypt.Length); 
    cStream.FlushFinalBlock(); 

    // Get an array of bytes from the 
    // MemoryStream that holds the 
    // encrypted data. 
    ret = mStream.ToArray(); 



    return ret; 

    catch (CryptographicException e) 

    //Console.WriteLine(”A Cryptographic error occurred: {0}”, e.Message); 
    return null; 





    /// <summary> 
    /// 将明文加密,返回密文 
    /// </summary> 
    /// <param name=”toEncrypt”>明文</param> 
    /// <param name=”encrypted”>密文</param> 
    /// <returns></returns> 
    public bool Encrypt(byte[] toEncrypt, out byte[] encrypted) 

    encrypted = null; 
    try 

    // Create a new MemoryStream using the passed 
    // array of encrypted data. 
    // Create a CryptoStream using the MemoryStream 
    // and the passed key and initialization vector (IV). 
    using (MemoryStream mStream = new MemoryStream()) 
    using (CryptoStream cStream = new CryptoStream(mStream, 
    new TripleDESCryptoServiceProvider().CreateEncryptor(byteKey, byteIV), 
    CryptoStreamMode.Write)) 


    // Write the byte array to the crypto stream and flush it. 
    cStream.Write(toEncrypt, 0, toEncrypt.Length); 
    cStream.FlushFinalBlock(); 

    // Get an array of bytes from the 
    // MemoryStream that holds the 
    // encrypted data. 
    encrypted = mStream.ToArray(); 


    return true; 

    catch (CryptographicException e) 

    //Console.WriteLine(”A Cryptographic error occurred: {0}”, e.Message); 
    return false; 






    /// <summary> 
    /// 将明文加密,返回 Base64 字符串 
    /// </summary> 
    /// <param name=”Data”></param> 
    /// <returns></returns> 
    public string EncryptToString(string Data) 

    try 

    string base64String = string.Empty; 

    using (MemoryStream mStream = new MemoryStream()) 
    using (CryptoStream cStream = new CryptoStream(mStream, 
    new TripleDESCryptoServiceProvider().CreateEncryptor(byteKey, byteIV), 
    CryptoStreamMode.Write)) 


    byte[] toEncrypt = new ASCIIEncoding().GetBytes(Data); 

    cStream.Write(toEncrypt, 0, toEncrypt.Length); 
    cStream.FlushFinalBlock(); 

    byte[] ret = mStream.ToArray(); 

    base64String = Convert.ToBase64String(ret); 


    return base64String; 

    catch (CryptographicException e) 

    return null; 





    /// <summary> 
    /// 将密文解密,返回明文 
    /// </summary> 
    /// <param name=”Data”>密文</param> 
    /// <returns>明文</returns> 
    public bool Decrypt(byte[] Data, out string decrypted) 

    decrypted = string.Empty; 
    try 


    using (MemoryStream msDecrypt = new MemoryStream(Data)) 
    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, 
    new TripleDESCryptoServiceProvider().CreateDecryptor(byteKey, byteIV), 
    CryptoStreamMode.Read)) 


    byte[] fromEncrypt = new byte[Data.Length]; 

    // Read the decrypted data out of the crypto stream 
    // and place it into the temporary buffer. 
    csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length); 

    decrypted = Encoding.UTF8.GetString(fromEncrypt);//new ASCIIEncoding().GetString(fromEncrypt); 

    return true; 


    catch (CryptographicException e) 

    return false; 




    using System;
    using System.Data;
    using System.Configuration;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;
    using System.Text;
    using System.Security.Cryptography;
    using System.IO;

    public class CryptoService
    {
    /// <summary>
    /// 加密的密钥
    /// </summary>
    string sKey = ”22362E7A9285DD53A0BBC2932F9733C505DC04EDBFE00D70”;
    string sIV = ”1E7FA9231E7FA923”;

    byte[] byteKey;
    byte[] byteIV;

    /// <summary>
    /// 加密向量
    /// </summary>
    static byte[] bIV ={ 1, 2, 3, 4, 5, 6, 7, 8 };

    public CryptoService()
    { }

    public CryptoService(string key, string IV)
    {
    sKey = key;
    sIV = IV;

    byteKey = CryptoHelper.HexStringToByteArray(sKey);
    byteIV = CryptoHelper.HexStringToByteArray(sIV);
    }

    /// <summary>
    /// 将明文加密,返回密文
    /// </summary>
    /// <param name=”Data”>要加密的字串</param>
    /// <returns></returns>
    public byte[] Encrypt(string Data)
    {
    try
    {
    byte[] ret;

    using (MemoryStream mStream = new MemoryStream())
    using (CryptoStream cStream = new CryptoStream(mStream,
    new TripleDESCryptoServiceProvider().CreateEncryptor(byteKey, byteIV),
    CryptoStreamMode.Write))
    {

    byte[] toEncrypt = new ASCIIEncoding().GetBytes(Data);

    // Write the byte array to the crypto stream and flush it.
    cStream.Write(toEncrypt, 0, toEncrypt.Length);
    cStream.FlushFinalBlock();

    // Get an array of bytes from the 
    // MemoryStream that holds the 
    // encrypted data.
    ret = mStream.ToArray();

    }

    return ret;
    }
    catch (CryptographicException e)
    {
    //Console.WriteLine(”A Cryptographic error occurred: {0}”, e.Message);
    return null;
    }

    }


    /// <summary>
    /// 将明文加密,返回密文
    /// </summary>
    /// <param name=”toEncrypt”>明文</param>
    /// <param name=”encrypted”>密文</param>
    /// <returns></returns>
    public bool Encrypt(byte[] toEncrypt, out byte[] encrypted)
    {
    encrypted = null;
    try
    {
    // Create a new MemoryStream using the passed 
    // array of encrypted data.
    // Create a CryptoStream using the MemoryStream 
    // and the passed key and initialization vector (IV).
    using (MemoryStream mStream = new MemoryStream())
    using (CryptoStream cStream = new CryptoStream(mStream,
    new TripleDESCryptoServiceProvider().CreateEncryptor(byteKey, byteIV),
    CryptoStreamMode.Write))
    {

    // Write the byte array to the crypto stream and flush it.
    cStream.Write(toEncrypt, 0, toEncrypt.Length);
    cStream.FlushFinalBlock();

    // Get an array of bytes from the 
    // MemoryStream that holds the 
    // encrypted data.
    encrypted = mStream.ToArray();
    }

    return true;
    }
    catch (CryptographicException e)
    {
    //Console.WriteLine(”A Cryptographic error occurred: {0}”, e.Message);
    return false;
    }

    }

    /// <summary>
    /// 将明文加密,返回 Base64 字符串
    /// </summary>
    /// <param name=”Data”></param>
    /// <returns></returns>
    public string EncryptToString(string Data)
    {
    try
    {
    string base64String = string.Empty;

    using (MemoryStream mStream = new MemoryStream())
    using (CryptoStream cStream = new CryptoStream(mStream,
    new TripleDESCryptoServiceProvider().CreateEncryptor(byteKey, byteIV),
    CryptoStreamMode.Write))
    {

    byte[] toEncrypt = new ASCIIEncoding().GetBytes(Data);

    cStream.Write(toEncrypt, 0, toEncrypt.Length);
    cStream.FlushFinalBlock();

    byte[] ret = mStream.ToArray();

    base64String = Convert.ToBase64String(ret);
    }

    return base64String;
    }
    catch (CryptographicException e)
    {
    return null;
    }

    }


    /// <summary>
    /// 将密文解密,返回明文
    /// </summary>
    /// <param name=”Data”>密文</param>
    /// <returns>明文</returns>
    public bool Decrypt(byte[] Data, out string decrypted)
    {
    decrypted = string.Empty;
    try
    {

    using (MemoryStream msDecrypt = new MemoryStream(Data))
    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt,
    new TripleDESCryptoServiceProvider().CreateDecryptor(byteKey, byteIV),
    CryptoStreamMode.Read))
    {

    byte[] fromEncrypt = new byte[Data.Length];

    // Read the decrypted data out of the crypto stream
    // and place it into the temporary buffer.
    csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);

    decrypted = Encoding.UTF8.GetString(fromEncrypt);//new ASCIIEncoding().GetString(fromEncrypt);

    return true;
    }
    }
    catch (CryptographicException e)
    {
    return false;
    }
    }

    }

    PostService.cs

    using System; 
    using System.Collections.Generic; 
    using System.Text; 

    public class PostService 

    private System.Collections.Specialized.NameValueCollection Inputs = new System.Collections.Specialized.NameValueCollection(); 
    public string Url = ””; 
    public string Method = ”post”; 
    public string FormName = ”form1”; 

    /// <summary> 
    /// 添加需要提交的名和值 
    /// </summary> 
    /// <param name=”name”></param> 
    /// <param name=”value”></param> 
    public void Add(string name, string value) 

    Inputs.Add(name, value); 


    /// <summary> 
    /// 以输出Html方式POST 
    /// </summary> 
    public void Post() 

    System.Web.HttpContext.Current.Response.Clear(); 

    string html = string.Empty; 

    html += (”<html><head>”); 
    html += (string.Format(”</head><body onload=”document.{0}.submit()”>”, FormName)); 
    html += (string.Format(”<form name=”{0}” method=”{1}” action=”{2}” >”, FormName, Method, Url)); 
    try 

    for (int i = 0; i < Inputs.Keys.Count; i++) 

    html += (string.Format(”<input name=”{0}” type=”hidden” value=”{1}”>”, Inputs.Keys[i], Inputs[Inputs.Keys[i]])); 

    html += (”</form>”); 
    html += (”</body></html>”); 

    System.Web.HttpContext.Current.Response.Write(html); 
    System.Web.HttpContext.Current.Response.End(); 

    catch (Exception ee) 

    // 



    using System;
    using System.Collections.Generic;
    using System.Text;

    public class PostService
    {
    private System.Collections.Specialized.NameValueCollection Inputs = new System.Collections.Specialized.NameValueCollection();
    public string Url = ””;
    public string Method = ”post”;
    public string FormName = ”form1”;

    /// <summary>
    /// 添加需要提交的名和值
    /// </summary>
    /// <param name=”name”></param>
    /// <param name=”value”></param>
    public void Add(string name, string value)
    {
    Inputs.Add(name, value);
    }

    /// <summary>
    /// 以输出Html方式POST
    /// </summary>
    public void Post()
    {
    System.Web.HttpContext.Current.Response.Clear();

    string html = string.Empty;

    html += (”<html><head>”);
    html += (string.Format(”</head><body onload=”document.{0}.submit()”>”, FormName));
    html += (string.Format(”<form name=”{0}” method=”{1}” action=”{2}” >”, FormName, Method, Url));
    try
    {
    for (int i = 0; i < Inputs.Keys.Count; i++)
    {
    html += (string.Format(”<input name=”{0}” type=”hidden” value=”{1}”>”, Inputs.Keys[i], Inputs[Inputs.Keys[i]]));
    }
    html += (”</form>”);
    html += (”</body></html>”);

    System.Web.HttpContext.Current.Response.Write(html);
    System.Web.HttpContext.Current.Response.End();
    }
    catch (Exception ee)
    {
    //
    }
    }
    }


    SSORequest.cs

    using System; 
    using System.Data; 
    using System.Configuration; 
    using System.Web; 
    using System.Web.Security; 
    using System.Web.UI; 
    using System.Web.UI.WebControls; 
    using System.Web.UI.WebControls.WebParts; 
    using System.Web.UI.HtmlControls; 

    [Serializable] 
    public class SSORequest : MarshalByRefObject 

    public string IASID; //各独立站点标识ID 
    public string TimeStamp; //时间戳 
    public string AppUrl; //各独立站点的访问地址 
    public string Authenticator; //各独立站点的 Token 

    public string UserAccount; //账号 
    public string Password; //密码 

    public string IPAddress; //IP地址 

    //为ssresponse对象做准备 
    public string ErrorDescription = ”认证失败”; //用户认证通过,认证失败,包数据格式不正确,数据校验不正确 
    public int Result = -1; 

    public SSORequest() 





    /// <summary> 
    /// 获取当前页面上的SSORequest对象 
    /// </summary> 
    /// <param name=”CurrentPage”></param> 
    /// <returns></returns> 
    public static SSORequest GetRequest(Page CurrentPage) 

    SSORequest request = new SSORequest(); 
    request.IPAddress = CurrentPage.Request.UserHostAddress; 
    request.IASID = CurrentPage.Request[”IASID”].ToString();// Request本身会Decode 
    request.UserAccount = CurrentPage.Request[”UserAccount”].ToString();//this.Text 
    request.Password = CurrentPage.Request[”Password”].ToString(); 
    request.AppUrl = CurrentPage.Request[”AppUrl”].ToString(); 
    request.Authenticator = CurrentPage.Request[”Authenticator”].ToString(); 
    request.TimeStamp = CurrentPage.Request[”TimeStamp”].ToString(); 
    return request; 


    using System;
    using System.Data;
    using System.Configuration;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;

    [Serializable]
    public class SSORequest : MarshalByRefObject
    {
    public string IASID; //各独立站点标识ID
    public string TimeStamp; //时间戳
    public string AppUrl; //各独立站点的访问地址
    public string Authenticator; //各独立站点的 Token

    public string UserAccount; //账号
    public string Password; //密码

    public string IPAddress; //IP地址

    //为ssresponse对象做准备
    public string ErrorDescription = ”认证失败”; //用户认证通过,认证失败,包数据格式不正确,数据校验不正确
    public int Result = -1;

    public SSORequest()
    {

    }


    /// <summary>
    /// 获取当前页面上的SSORequest对象
    /// </summary>
    /// <param name=”CurrentPage”></param>
    /// <returns></returns>
    public static SSORequest GetRequest(Page CurrentPage)
    {
    SSORequest request = new SSORequest();
    request.IPAddress = CurrentPage.Request.UserHostAddress;
    request.IASID = CurrentPage.Request[”IASID”].ToString();// Request本身会Decode
    request.UserAccount = CurrentPage.Request[”UserAccount”].ToString();//this.Text
    request.Password = CurrentPage.Request[”Password”].ToString();
    request.AppUrl = CurrentPage.Request[”AppUrl”].ToString();
    request.Authenticator = CurrentPage.Request[”Authenticator”].ToString();
    request.TimeStamp = CurrentPage.Request[”TimeStamp”].ToString();
    return request;
    }
    }

    配置web.config

    <authentication mode=”Forms”> 
    <forms name=”.AspxFormAuth” loginUrl=”Default.aspx” defaultUrl=”center.html” protection=”All” path=”/” timeout=”60”> 
    </forms> 
    </authentication> 
    <authorization> 
    <!--拒绝所有匿名用户--> 
    <deny users=”?”/> 
    </authorization> 
    <authentication mode=”Forms”>
    <forms name=”.AspxFormAuth” loginUrl=”Default.aspx” defaultUrl=”center.html” protection=”All” path=”/” timeout=”60”>
    </forms>
    </authentication>
    <authorization>
    <!--拒绝所有匿名用户-->
    <deny users=”?”/>
    </authorization>

    最后效果如下:登录总站后,各站点之间无需再登录,可以互相访问。


    另外,注销登录后,访问站点1 http://localhost/Site1/Default.aspx ,会自动跳转到主站登录页面 http://localhost/MasterSite/Default.aspx ,同样访问站点2 http://localhost/Site2/Default.aspx 也会转到主站登录页面。从主站登录后,分别访问站点1和站点2。

    在IIS配置虚拟目录MasterSite Site1 Site2,当然你也可以新建站点MasterSite Site1 Site2,修改hosts表
    127.0.0.1 www.MasterSite.com

    127.0.0.1 www.Site1.com

    127.0.0.1 www.Site2.com

    源代码下载:http://download.csdn.net/source/1571879

  • 相关阅读:
    牛客小白月赛3 I 排名【结构体排序/较复杂/细节】
    牛客小白月赛3 F 异或【区间交集】
    牛客小白月赛3 B 躲藏【动态规划/字符串出现cwbc子序列多少次】
    陕西师范大学第七届程序设计竞赛网络同步赛 I 排队排队排队【数组任一位可以移动到队头,最少移动几次增序/数组指针操作】
    陕西师范大学第七届程序设计竞赛网络同步赛 J 黑猫的小老弟【数论/法拉数列/欧拉函数】
    陕西师范大学第七届程序设计竞赛网络同步赛D ZQ的睡前故事【约瑟夫环1-N数到第k个出队,输出出队顺序/ STL模拟】
    陕西师范大学第七届程序设计竞赛网络同步赛 C iko和她的糖【贪心/ STL-优先队列/ 从1-N每个点有能量补充,每段有消耗,选三个点剩下最多能量】
    陕西师范大学第七届程序设计竞赛网络同步赛 F WWX的礼物【数学/k进制下x^n的位数/log】
    NYOJ 71 乘船问题【贪心】
    hdu 5273 Dylans loves sequence
  • 原文地址:https://www.cnblogs.com/wwy224y/p/3824816.html
Copyright © 2020-2023  润新知