偷懒小工具 - 通用单点登录类(可跨域)

写在前面的话:上次发布过一篇同样标题的文章。但是因为跨域方面做得不太理想。

我进行了修改,并重新分享给大家。想看原来的文,可点击上方的超链接。

目的

目的很明确,就是搭建单点登录的帮助类,并且是一贯的极简风格(调用方法保持5行以内)。

并且与其他类库,关联性降低。所以,不使用WebAPI或者WebService等。

思路

因为上次有朋友说,光看见一堆代码,看不见具体思路。所以,这次分享,我把思路先写出来。

懒得看实现代码的朋友,可直接查看“思路”这个子标题。

同时如果有好的想法,请修改后在github上推给我。Talk is cheap,Show me the code

思路

同域

同域需要考虑的问题比较少。只需要考虑,MVC和WebForm的Request如何获取即可。

实现流程图如下

1. 因为是使用同样的Cookie所以名称和加密方式必须一致。

2. 需要设置登录成功后,回跳的网址。因为Forms身份认证的ReturnURL不能获得请求原网址。

3. 剩下的就如何所示了。不明白的可以追问,我就不细说了。

跨域

跨域除了需要考虑同域的问题外,还需要考虑状态共享。因为同源策略问题,故此使用JSONP

1. 因为不是Cookie共享,所以只需要设置相同的加密方法即可。

2. 需要在认证网站,添加可登录的其他网站集合,使用逗号分隔。

3. 需要在其他网站,创建一个Login页面并调用帮助类的验证方法。配置认证网站URL。

4. 当认证网站登录成功后,会根据配置的其他网站,给他们发送JSONP请求,让他们自动登录。

5. 注销同理。JSONP请求方式,可参考这篇文章:jsonp详解。使用的就是添加js标签的方式。

至此,思路说明结束。不明白的可以追问。


详细设计

简介

整个类库格式如下,我尽量进行了重构,让各位看着方便一些。因为懒所以只是尽量重构。

SSO.js:跨域单点登录,需要在登录页面引用的Javascript脚本。

SSOCrossDomain:跨域帮助类

SSOSameDomain:同域帮助类

App.config:跨域帮助类,涉及到的配置示例

需要在认证网站和其他网站中,同时引用这个类。并根据自己的需求,看调用哪个帮助类。

使用方法

首先,我们创建如下结构的解决方案来进行演示。

Authorize:是WebForm的认证网站,使用MVP的PV模式搭建。其他的均为需要共享的网站。

MVC1:是MVC的认证网站。认证网站均实现了,最简单的登录功能。

同域

首先说一下同域如何使用。

1. 我们需要配置相同的身份验证。那么我们在Web.Config中,写上如下代码。

<system.web>
    <compilation debug="true" targetFramework="4.6.1" />
    <authentication mode="Forms">
      <forms loginUrl="~/Login.aspx" name="CookiesTest" cookieless="UseCookies"></forms>
    </authentication>
    <authorization>
      <deny users="?" />
    </authorization>
    <machineKey validationKey="5029E82E1779497186D46F83D78FAD3211D46F83D78FAD" validation="SHA1" decryptionKey="5029E82E1779497186D46F83D78FAD3211D46F83D78FAD" decryption="DES" />
  </system.web>

Authorize

<system.web>
    <compilation debug="true" targetFramework="4.6.1" />
    <authentication mode="Forms">
      <forms loginUrl="http://localhost:51666/Login.aspx?link=http://localhost:56757/WebForm1.aspx" name="CookiesTest" cookieless="UseCookies"></forms>
      <!--<forms loginUrl="~/Login.aspx" name="CookieWeb1" cookieless="UseCookies"></forms>-->
    </authentication>
    <authorization>
      <deny users="?" />
    </authorization>
    <machineKey validationKey="5029E82E1779497186D46F83D78FAD3211D46F83D78FAD" validation="SHA1" decryptionKey="5029E82E1779497186D46F83D78FAD3211D46F83D78FAD" decryption="DES" />
  </system.web>

Web1

配置东西分别为:Forms认证,禁止匿名用户访问,配置单点登录加密方式。

其中Web1的Forms认证,指向的就是Authorize,并且使用link当做后缀,进行成功后跳转。

2. 需要在Authorize网站中,添加登录页面,并添加登录后的调用方法。

/// <summary>
        /// 用户登录方法
        /// </summary>
        private void LoginView_Submit(object sender, AuthorizeEventArgs e)
        {
            string userName = LoginView.UserName;
            string password = LoginView.Password;
            if (ValidationUserInfo(userName, password))
            {
                //同域单点登录
                SSOSameDomain sso = new SSOSameDomain(e.Page);
                sso.LogIn("CookiesTest", new TimeSpan(0, 1, 0), userName);

                ////跨域单点登录
                //SSOCrossDomain cross = new SSOCrossDomain(e.Page);
                //cross.LogIn("CookiesTest", new TimeSpan(0, 1, 0), userName);
            }
        }

Authorize

SSOSameDomain,分别可以接受Page和HttpContextBase,作为读取Request的媒介。

所以各位如果不用MVP,可实例化时直接this。

LogIn登录方法,需要传递配置的Cookie名称、过期时间和需要保存的内容。

3. 配置注销功能,在点击注销后,执行如下方法。

protected void SignOut_Click(object sender, EventArgs e)
        {
            new SSOSameDomain(this).LogOut();
            //new SSOCrossDomain(this).LogOut();
        }

注销

4. 获取用户内容,可以调用帮助类的GetUserData方法。传递Cookie名称,即可获取对应内容。

protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                if (User.Identity.IsAuthenticated)
                {
                    var result = new SSOSameDomain(this).GetUserData("CookiesTest");
                    txtUserData.Text = result;
                    //SSOCrossDomain cross = new SSOCrossDomain(this);
                    //txtUserData.Text = cross.GetUserData("CookieWeb1");
                }
            }
        }

获取用户内容

至此,我们已经完成了同域的单点登录。

跨域

跨域因为需要验证,所以会比同域操作多几步。注意:每个网站都必须有类似Login.aspx页面用作登录存储。

1. 首先配置相同的加密方式,因为我们的JSONP传递的是密文,所以解密方式必须一致。

<system.web>
    <compilation debug="true" targetFramework="4.6.1" />
    <authentication mode="Forms">
      <forms loginUrl="~/Login.aspx" name="CookiesTest" cookieless="UseCookies"></forms>
    </authentication>
    <authorization>
      <deny users="?" />
    </authorization>
    <machineKey validationKey="5029E82E1779497186D46F83D78FAD3211D46F83D78FAD" validation="SHA1" decryptionKey="5029E82E1779497186D46F83D78FAD3211D46F83D78FAD" decryption="DES" />
  </system.web>

Authorize

其他网站的Forms认证页面,都指向本地的Login.aspx。注意加密方式必须一致,不然无法解密。

2. 认证网站设置可登录的网址集合,在配置文件中添加集合,使用逗号分隔。

<appSettings>
    <add key="LoginUrl" value="http://localhost:56757/Login.aspx,http://localhost/Web2/Login.aspx" />
  </appSettings>

LoginUrl

3. 其他网站设置统一认证的网址,并添加成功后跳转的地址。

<appSettings>
    <add key="AuthorizeUrl" value="http://localhost:51666/Login.aspx?link=http://localhost:56757/WebForm1.aspx" />
  </appSettings>

AuthorizeUrl

至此,配置结束,我们接下来说一下如何调用。

4. 认证网站,添加验证登录和登录方法。

public void Initialize(Page page)
        {
            SSOCrossDomain cross = new SSOCrossDomain(page);
            cross.ValidationLogIn("CookiesTest", new TimeSpan(0, 1, 0));
        }

        /// <summary>
        /// 用户登录方法
        /// </summary>
        private void LoginView_Submit(object sender, AuthorizeEventArgs e)
        {
            string userName = LoginView.UserName;
            string password = LoginView.Password;
            if (ValidationUserInfo(userName, password))
            {
                ////同域单点登录
                //SSOSameDomain sso = new SSOSameDomain(e.Page);
                //sso.LogIn("CookiesTest", new TimeSpan(0, 1, 0), userName);

                //跨域单点登录
                SSOCrossDomain cross = new SSOCrossDomain(e.Page);
                cross.LogIn("CookiesTest", new TimeSpan(0, 1, 0), userName);
            }
        }

认证网站

Initialize:是Login.aspx页面初始化执行的方法,我们调用帮助类的ValidationLogin,验证是否登录。

Login:调用帮助类的Login方法,可以保存登录状态,并向其他网站进行发送状态。

5. 其他网站,添加验证登录方法。

protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                SSOCrossDomain cross = new SSOCrossDomain(this);
                cross.ValidationLogIn("CookieWeb1", new TimeSpan(0, 2, 0));
            }
        }

其他网站

ValidationLogIn :验证登录方法,传递参数:本地存储的Cookie名称,过期时间。

6. 其他网站,添加注销方法和获取登录内容。

protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                if (User.Identity.IsAuthenticated)
                {
                    var result = new SSOSameDomain(this).GetUserData("CookiesTest");
                    txtUserData.Text = result;
                    //SSOCrossDomain cross = new SSOCrossDomain(this);
                    //txtUserData.Text = cross.GetUserData("CookieWeb1");
                }
            }
        }

        protected void SignOut_Click(object sender, EventArgs e)
        {
            //new SSOSameDomain(this).LogOut();
            new SSOCrossDomain(this).LogOut();
        }

注销和获取

至此,我们已经完成了跨域的单点登录。每个调用,不超过5行代码,极简风格。

MVC方法类似,可以参考下方源码。

代码实现

Operation

Operation用来处理跟Request和Response挂钩的操作。我目前没有找到WebForm和MVC公用的类。

故此使用抽象工厂来实现此类操作。此处,我一直不是很满意,希望有其他想法的可以告知。

1. 定义抽象类。

/// <summary>
    /// 单点登录操作工厂
    /// </summary>
    public abstract class Operation
    {
        /// <summary>
        /// 执行授权的脚本
        /// </summary>
        public string PerformJavascript { get; set; }

        /// <summary>
        /// 获取参数
        /// </summary>
        /// <param name="request">参数名</param>
        /// <returns>参数值</returns>
        public abstract string GetRequest(string request);

        /// <summary>
        /// 设置Cookie
        /// </summary>
        /// <param name="cookie">Cookie实体</param>
        public abstract void SetCookie(HttpCookie cookie);

        /// <summary>
        /// 获取Cookie值
        /// </summary>
        /// <param name="cookieName">Cookie名称</param>
        public abstract HttpCookie GetCookie(string cookieName);

        /// <summary>
        /// 重定向制定页面
        /// </summary>
        /// <param name="url">目标URL</param>
        public abstract void Redirect(string url);

        /// <summary>
        /// 输出指定内容
        /// </summary>
        /// <param name="text">内容</param>
        public abstract void PerformJs(string text);

        /// <summary>
        /// 获取当前URL
        /// </summary>
        /// <returns></returns>
        public abstract Uri Uri();
    }

Operation

2. 定义WebForm的操作类。

/// <summary>
    /// WebForm操作方法
    /// </summary>
    public class OperationPage : Operation
    {
        public Page Page { get; set; }

        public OperationPage(Page page)
        {
            Page = page;
        }

        public override string GetRequest(string request)
        {
            string result = Page.Request[request];
            return result ?? "";
        }

        public override void SetCookie(HttpCookie cookie)
        {
            Page.Response.Cookies.Add(cookie);
        }

        public override HttpCookie GetCookie(string cookieName)
        {
            return Page.Request.Cookies[cookieName];
        }

        public override void Redirect(string url)
        {
            Page.Response.Redirect(url);
        }

        public override void PerformJs(string text)
        {
            Page.ClientScript.RegisterStartupScript(Page.ClientScript.GetType(), "LogIn", text);
        }

        public override Uri Uri()
        {
            return new Uri(Page.Request.Url.ToString());
        }
    }

OperationPage

3. 定义MVC的操作类

/// <summary>
    /// MVC操作方法
    /// </summary>
    public class OperationHttpContext : Operation
    {
        public HttpContextBase Context { get; set; }

        public OperationHttpContext(HttpContextBase context)
        {
            Context = context;
        }

        public override string GetRequest(string request)
        {
            return Context.Request[request];
        }

        public override void SetCookie(HttpCookie cookie)
        {
            Context.Response.Cookies.Add(cookie);
        }

        public override HttpCookie GetCookie(string cookieName)
        {
            return Context.Request.Cookies[cookieName];
        }

        public override void Redirect(string url)
        {
            Context.Response.Redirect(url);
        }

        public override void PerformJs(string text)
        {
            text = text.Replace("<script>", "");
            text = text.Replace("</script>", "");
            PerformJavascript = text;
        }

        public override Uri Uri()
        {
            return new Uri(Context.Request.Url.ToString());
        }
    }

OperationHttpContext

我们通过帮助类的构造函数,对Operation进行初始化。

/// <summary>
        /// HTTP状态操作
        /// </summary>
        public Operation Operation { get; set; }

        public SSOSameDomain(HttpContextBase context)
        {
            Operation = new OperationHttpContext(context);
        }

        public SSOSameDomain(Page page)
        {
            Operation = new OperationPage(page);
        }

初始化

同域

同域帮助类,需要公开三个功能:LogIn,LogOut,GetUserData。此处如果有其他需也可做成接口。

public class SSOSameDomain
    {
        /// <summary>
        /// HTTP状态操作
        /// </summary>
        public Operation Operation { get; set; }

        public SSOSameDomain(HttpContextBase context)
        {
            Operation = new OperationHttpContext(context);
        }

        public SSOSameDomain(Page page)
        {
            Operation = new OperationPage(page);
        }

        /// <summary>
        /// 用户登录
        /// </summary>
        public void LogIn(string cookieName, TimeSpan overdue, string userData)
        {
            FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(2, cookieName, DateTime.Now, DateTime.Now.Add(overdue), true, userData);
            CreateCookie(ticket);
            RedirectPage();
        }

        /// <summary>
        /// 用户注销
        /// </summary>
        public void LogOut()
        {
            FormsAuthentication.SignOut();
            FormsAuthentication.RedirectToLoginPage();
        }

        /// <summary>
        /// 获取登录信息
        /// </summary>
        public string GetUserData(string cookieName)
        {
            string result = Operation.GetCookie(cookieName)?.Value;
            return result != null ? FormsAuthentication.Decrypt(result).UserData : "";
        }

        /// <summary>
        /// 创建Cookie
        /// </summary>
        private void CreateCookie(FormsAuthenticationTicket ticket)
        {
            HttpCookie cookie = new HttpCookie(ticket.Name, FormsAuthentication.Encrypt(ticket));
            cookie.Expires = ticket.Expiration;
            Operation.SetCookie(cookie);
        }

        /// <summary>
        /// 登录成功跳转
        /// </summary>
        private void RedirectPage()
        {
            if (!string.IsNullOrEmpty(Operation.GetRequest("link")))
            {
                Operation.Redirect(Operation.GetRequest("link"));
                return;
            }
            if (!string.IsNullOrEmpty(Operation.GetRequest("ReturnUrl")))
            {
                Operation.Redirect(Operation.GetRequest("ReturnUrl"));
                return;
            }
            Operation.Redirect("/");
        }

    }

同域帮助类

同域的非常简单,我不讲解什么了。

跨域

跨域帮助类,需要公开四个功能,除了同域的三个功能外,添加ValidationLogIn验证功能。

1. 首先,我们说一下如何实现的JSONP。我们创建了一个Js方法,然后从后端调用这个方法。

function LogIn() {
    var urlList = arguments;
    for (var i = 1; i < urlList.length; i++) {
        CreateScript(urlList[i]);
    }
    window.location.href = urlList[0];
}

function CreateScript(src) {
    $("<script><//script>").attr("src", src).appendTo("body")
}

SSO

方法一目了然,不多说了。使用这个加载script,就可以进行JSONP的访问。

我们接下来,一步一步过一下每个方法。

2. LogIn 用户登录

/// <summary>
        /// 用户登录授权
        /// <param name="userData">用户信息</param>
        /// </summary>
        public void LogIn(string cookieName, TimeSpan overdue, string userData, string redirectUrl = "")
        {
            FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(2, cookieName, DateTime.Now, DateTime.Now.Add(overdue), true, userData);
            CreateCookie(ticket);
            PerformJavascript("logIn", redirectUrl, userData);
        }

Login

分别就是:创建凭证、创建Cookie、发送JSONP请求

/// <summary>
        /// 执行前端js跳转,授权
        /// </summary>
        private void PerformJavascript(string logType, string redirectLink, string userData = "")
        {
            Uri uri = Operation.Uri();
            string redirectUrl = "";
            if (string.IsNullOrEmpty(redirectLink))
            {
                redirectUrl = GetPageUrl();
                //如果返回网址包含Http,则直接跳转。不包含则本网址内跳转
                if (!redirectUrl.Contains("http"))
                {
                    redirectUrl = uri.Scheme + "://" + uri.Authority + GetPageUrl();
                }
            }
            else
            {
                redirectUrl = redirectLink;
            }
            StringBuilder resultMethod = new StringBuilder("LogIn(‘" + redirectUrl + "‘,");
            foreach (string url in GetUrlList())
            {
                resultMethod.Append("‘");
                resultMethod.Append(string.Format("{0}?logType={1}&userData={2}", url, logType, userData));
                resultMethod.Append("‘,");
            }
            resultMethod.Remove(resultMethod.Length - 1, 1);
            resultMethod.Append(")");
            Operation.PerformJs("<script>" + resultMethod + "</script>");
        }

PerformJavascript

执行前端JS方法,内容分别是:获取成功跳转路径,拼接调用方法的Js,执行Js

3. LogOut 用户注销

/// <summary>
        /// 用户注销
        /// </summary>
        public void LogOut()
        {
            FormsAuthentication.SignOut();
            string loginUrl = ConfigurationManager.AppSettings["LoginUrl"];
            if (string.IsNullOrEmpty(loginUrl))
            {
                string authorizeUrl = ConfigurationManager.AppSettings["AuthorizeUrl"];
                Operation.Redirect(authorizeUrl + "&logType=logOut");
                return;
            }
            PerformJavascript("logOut", "");
        }

LogOut

分别就是:本地注销、远程发送注销请求到认证网站,执行Js

4. GetUserData 与同域类似,这里不贴代码了。

5. ValidationLogIn 验证登录用户,会判断请求的logType,来进行登录和注销的操作。

public void ValidationLogIn(string cookieName, TimeSpan overdue)
        {
            string logTypeParameter = Operation.GetRequest("logType");
            string redirectLink = Operation.GetRequest("link");
            if (string.IsNullOrEmpty(logTypeParameter))
            {
                string authorizeUrl = ConfigurationManager.AppSettings["AuthorizeUrl"];
                if (string.IsNullOrEmpty(authorizeUrl))
                {
                    return;
                }
                else
                {
                    Operation.Redirect(authorizeUrl);
                    return;
                }
            }
            SSOSameDomain sameDomain = new SSOSameDomain(HttpContextType);
            switch (logTypeParameter)
            {
                case "logIn":
                    sameDomain.LogIn(cookieName, overdue, Operation.GetRequest("userData"));
                    break;

                case "logOut":
                    FormsAuthentication.SignOut();
                    if (string.IsNullOrEmpty(redirectLink))
                    {
                        FormsAuthentication.RedirectToLoginPage();
                    }
                    else
                    {
                        Operation.Redirect(redirectLink);
                    }
                    break;

                default:
                    throw new InvalidOperationException("登录认证状态无效");
            }
        }

ValidationLogIn

开源地址:https://github.com/chenxygx/SSOGeneral

开发过程中,思路是最重要的。但是还需要用实际的代码来验证你的思路。毕竟语言是廉价的。

最后的话

这个偷懒小工具系列,都是我没事干写的东西,并不是工作内容。我分享也只是用自己的行动,支持开源精神。

如果能帮到您,我会很高兴的。如果帮不到您,右上角就可以了。请大神们,不要拍砖哦~

时间: 2024-08-08 13:53:45

偷懒小工具 - 通用单点登录类(可跨域)的相关文章

偷懒小工具 - SSO单点登录通用类(可跨域)(上)

目的  目的很明确,就是搭建单点登录的帮助类,并且是一贯的极简风格(调用方法保持5行以内). 并且与其他类库,关联性降低.所以,不使用WebAPI或者WebService等. 思路   因为上次有朋友说,光看见一堆代码,看不见具体思路.所以,这次分享,我把思路先写出来. 懒得看实现代码的朋友,可直接查看"思路"这个子标题. 同时如果有好的想法,请修改后在github上推给我.Talk is cheap,Show me the code 思路   同域 同域需要考虑的问题比较少.只需要考

【原】保存一下之前spring-session的redis单点登录配置文件【跨域】

由于先前在调试项目的时候需要做单点,但是项目是基于spring-session老版本做的单点登录,没有实现跨域登录,因为只是针对相同域名下的用户缓存进行存储而已,例如 http://127.0.0.1/wap 和 http://127.0.0.1/wap2 ,这样的话只要在 第一个域名登录后再去第二2个域名进行用户登录,则无需重复登录,但是如果是 http://127.0.0.1/wap 和 http://192.168.1/wap2 这样就没办法找到session. <?xml version

单点登录之ajax跨域实现

需求:相同根域名或不同根域名的两个域名,实现单点登录登出 原理: 以b站为例,b站的账号登录域名为passport.bilibili.com.主站为www.bilibili.com,游戏站为www.biligame.com, 获取登录用户信息接口为http://api.bilibili.com/nav?callback=jQuery17209622933453583296_1497257608944&type=jsonp&_=1497257609114, 1.用户访问www.bilibil

偷懒小工具 - Excel导出公共类

说明 最近接了一个任务,就是做一个列表的Excel导出功能.并且有很多页面都会使用这个功能. 导出的Excel大体格式如图 很简单的列表,标题加背景色,然后不同类型,显示方式不一样.对齐方式不一样.不同页面除了内容以外,大体形式都差不多. 当时本来是想直接用NPOI,IRow ICell.这样进行拼接页面,最简单也最方便. 但是很多页面,都进行这种类似的设计.我实在是懒得做这种重复功能.所以花了一点时间,整理了一下帮助类. 使用 做好这个帮助类以后只要进行两点调用 1.制作导出Excel的数据模

单点登录 关于Cookie跨域的问题

public void ProcessRequest(HttpContext context) { HttpCookie cookie = new HttpCookie("name", "www.Admin10000.com"); cookie.Domain = "test2.com"; cookie.Path = "/"; cookie.Expires = DateTime.Now.AddMinutes(10000); co

php网站单点登录--卷一(跨二级域名)

Session主要分两部分: 一,是Session数据,该数据默认情况下是存放在服务器的tmp文件下的,php.ini中可以更改. 二,是标志着Session数据的Session Id,Session ID,就是那个 Session 文件的文件名,Session ID 是随机生成的,因此能保证唯一性和随机性,确保 Session 的安全.一般如果没有设置 Session 的生存周期,则 Session ID 存储在内存中,关闭浏览器后该 ID 自动注销,重新请求该页面后,重新注册一个 sessi

python练习小工具之模拟登录系统

一.题目 模拟写一个登录系统,要求 : 每个用户都有三次登录机会,超过三次锁定! 用户锁定不影响其他用户 二.代码: #coding:utf-8 Count = {"san": ["123456", 3], "Ling": ["6666",3]} #值中第一列是密码,第二列是用户状态 while True:     User=raw_input("用户名:").strip()     Passwd=raw_

单点登录系统(SSO)的开发思路

单点登录并不是一个新鲜的玩意儿,比较官方的解释是企业业务整合的解决方案之一,通俗来讲SSO就是一个通用的用户中心,国内比较流行的UCenter就是一套单点登录解决方案.而近期以CSDN明文存储用户密码并泄露用户信息开始的各大网站争先恐后的泄露自己的用户数据库除了暴露了这些网站的良心和智商外,如何设计用户中心已成为架构师们的热点话题之一.在最近一两年的项目经验中有幸接触到各种平台的单点登录系统的开发,所以借此机会总结下B/S架构的单点登录系统的开发经验. 单点登录系统的类别 就目前比较流行的应用来

多平台的网站实现单点登录系统(SSO)的开发思路 让你的会员中心更加统一(参考资料)

单点登录并不是一个新鲜的玩意儿,比较官方的解释是企业业务整合的解决方案之一,通俗来讲SSO就是一个通用的用户中心,国内比较流行的UCenter就是一套单点登录解决方案.而近期以CSDN明文存储用户密码并泄露用户信息开始的各大网站争先恐后的泄露自己的用户数据库除了暴露了这些网站的良心和智商外,如何设计用户中心已成为架构师们的热点话题之一.在最近一两年的项目经验中有幸接触到各种平台的单点登录系统的开发,所以借此机会总结下B/S架构的单点登录系统的开发经验. 单点登录系统的类别 就目前比较流行的应用来