现在越来越多的系统迫于压力以及提高性能,很多站点都是采用多站点分布式运行,例如腾讯、新浪的站点就分成很多个频道,各个频道有独立的域名,独立的IP来支撑,这样一来各个站点之间就出现了统一认证的问题,也就是需要用户在一个站点登录,其他站点都能用的,且退出之后,各个站点都不能用,形成对用户的统一管理,避免了各个子系统之间的功能冗余。

本文就作者自己的一些使用过的设计方法进行整理。使用过的方式主要两种,一是认证中心被动认证,二是认证中心主动认证,本文将介绍被动认证模式。

一、认证流程

主要的流程是,用户第一次访问系统A的页面a,当前系统(系统A)状态还没有登录,所以只能跳转到认证中心进行登录,登录之后,认证中心跳转回来站点A的页面a,该用户现在已经拥有了Token(Token由认证中心根据用户名以及登录时间生成,这个是唯一的字符串,每次生成都会不同),但是站点A不知道此Token是否合法,所以系统A自动跳转到认证中心,拿该Token进行验证,验证通过再跳转回来站点A,此时站点知道用户的Token合法,允许访问页面a。此种验证方法有个小问题就是,访问站点A的每个页面都需要到认证中心认证所拥有的Token是否合法(因为有可能用户会在其中一个子系统退出,退出后用户的旧Token为非法Token)

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

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

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

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

来看实现代码:

SubWebSite:Default.aspx

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

<div>
        通过了认证,登录的用户名为<%=Session["UserName"] %>
            <asp:Button ID="btnSignOut" runat="server" Text="退出"
                onclick="btnSignOut_Click" />
        </div>
</form>
Default.aspx.cs:

protected void Page_Load(object sender, EventArgs e)
    {
        //当前子系统登录,且没有从认证中心取得Token
        if (Session["Token"] == null && Request.QueryString["Token"] == null)
            ReSignIn();//需要重新登录
        //Session和Url中都有Token
        if (Request.QueryString["Token"] != null && Session["Token"] != null)
        {
            //Url中的Token和Session中的Token不一致,Token已经失效
            string urlToken = Request.QueryString["Token"];
            string sessionToken = Request.QueryString["Token"];
            if (!urlToken.Equals(sessionToken))
            {
                ReSignIn();//需要重新登录
            }
            else
            {
                string userName = Request["UserName"];
                //在本地系统登录
                if (SignInSubSys(userName))
                {
                    Session["Token"] = string.Empty;
                    Response.End();//登录失败
                }
            }
        }
            //已经从认证中心取得Token,根据Token取认证中心取得用户信息
        else if (Request.QueryString["Token"] != null && Session["Token"] == null)
        {

Session["Token"] = Request.QueryString["Token"];
            Response.Redirect("Http://mowen:8000/UserCenter/GetUserInfo.aspx?ReturnUrl=" +
                              HttpUtility.UrlEncode(Request.Url.AbsoluteUri) + "&Token=" + Request.QueryString["Token"]);
        }
        else
        {
            ReSignIn();
        }
    }

/// <summary>
    /// 重新登录获取Token
    /// </summary>
    protected void ReSignIn()
    {
        Response.Redirect("http://mowen:8000/UserCenter/Default.aspx?ReturnUrl=" + HttpUtility.UrlEncode(Request.Url.AbsoluteUri));
    }

/// <summary>
    /// 在子站点进行登录
    /// </summary>
    /// <returns></returns>
    protected bool SignInSubSys(string userName)
    {
        if (!string.IsNullOrEmpty(userName))
        {
            Session["UserName"] = userName;
        }
        return false;
    }

/// <summary>
    /// 退出登录
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void btnSignOut_Click(object sender, EventArgs e)
    {
        //清空Session
        Session.Clear();
        //跳转到认证中心
        Response.Redirect("Http://mowen:8000/UserCenter/SignOut.aspx?ReturnUrl=" +
                          HttpUtility.UrlEncode(Request.Url.AbsoluteUri));
}

认证中心获取Token页面(Default.aspx.cs):
   protected void Page_Load(object sender, EventArgs e)
    {
        //来源的Url(此Http请求上一个请求,由上一个请求使用参数传递)
        var returnUrl = string.Empty;
        if (Request["ReturnUrl"] != null)
            returnUrl = HttpUtility.HtmlDecode(Request["ReturnUrl"].Trim());
        //根据Session来判断用户是否已经登录
        if(Session["UserName"] != null && Session["Token"] != null)
        {
            //附加上Token参数并告诉子系统当前登录的用户名
            if (returnUrl.IndexOf("?") > 0)
                returnUrl = returnUrl + "&Token=" + Session["Token"] + "&UserName=" + Session["UserName"];
            else
                returnUrl = returnUrl + "?Token=" + Session["Token"] + "&UserName=" + Session["UserName"];
            //已经登录
            //跳转到原地址并加上Token
            Response.Redirect(returnUrl);
        }
        else
        {
            //没有登录,跳转到登录页面
            Response.Redirect("SignIn.aspx?ReturnUrl=" + HttpUtility.UrlEncode(returnUrl));
        }
}

认证中心登录页面:

SignIn.aspx:
<form id="form1" runat="server">
         <div>
              用户名:<asp:TextBox ID="txtUserName" runat="server"></asp:TextBox><br />
              密码:<asp:TextBox ID="txtPwd" runat="server"></asp:TextBox><br />
              <asp:Button ID="btnSignIn" runat="server" Text="登录" onclick="btnSignIn_Click" />

</div>
</form>

SignIn.aspx.cs:
    protected void btnSignIn_Click(object sender, EventArgs e)
    {
        Session["UserName"] = txtUserName.Text.Trim();
        Session["Token"] =
            System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(
                txtUserName.Text.Trim() + DateTime.Now, "md5");
        //*************************************************************************
        //将Token和用户信息关联起来
        Hashtable htTokenInfo = null;
        if (Application["UserTokenInfo"] != null)
            htTokenInfo = (Hashtable)Application["UserTokenInfo"];
        if (htTokenInfo == null)
            htTokenInfo = new Hashtable();
        if (!htTokenInfo.ContainsKey(Session["Token"]))
            htTokenInfo.Add(Session["Token"], txtUserName.Text.Trim());
        else
            htTokenInfo[Session["Token"]] = txtUserName.Text.Trim();
        Application["UserTokenInfo"] = htTokenInfo;
        //*************************************************************************
         //来源的Url(此Http请求上一个请求,由上一个请求使用参数传递)

var returnUrl = string.Empty;
        if (Request["ReturnUrl"] != null)
            returnUrl = HttpUtility.HtmlDecode(Request["ReturnUrl"].Trim());
       returnUrl = BuildUrl(returnUrl, "Token", Session["Token"].ToString());

//跳转回去源地址
        Response.Redirect(returnUrl);
    }

/// <summary>
    /// 功能:url里有key的值,就替换为value,没有的话就追加.
    /// 执行过程:(1)、使用正则表达式匹配key
    ///           (2)、将匹配的Key的值替换为空的值
    ///           (3)、使用新值进行替换
    /// 作者:莫文
    /// 时间:2010-5-30 13:48
    /// Email:mowen@founder.com
    /// 
    /// </summary>
    /// <param name="url">要替换Url参数值的Url</param>
    /// <param name="paramText">要替换值的参数名</param>
    /// <param name="paramValue">要被替换的参数的值</param>
    /// <returns>替换参数值后的Url</returns>
    public static string BuildUrl(string url, string paramText, string paramValue)
    {
        //使用正则匹配所要查找的Key
        Regex reg = new Regex(string.Format("{0}=[^&]*", paramText), RegexOptions.IgnoreCase);
        Regex reg1 = new Regex("[&]{2,}", RegexOptions.IgnoreCase);
        //将旧值替换为空
        string _url = reg.Replace(url, "");
        //新值
        if (_url.IndexOf("?") == -1)
            _url += string.Format("?{0}={1}", paramText, paramValue);//?
        else
            _url += string.Format("&{0}={1}", paramText, paramValue);//&
        //将新值替换到Url中
        _url = reg1.Replace(_url, "&");
        //将多余的&字符清除
        _url = _url.Replace("?&", "?");
        return _url;
    }

认证中心验证Token验证页面(GetUserInfo.aspx.cs):
    protected void Page_Load(object sender, EventArgs e)
    {
        Hashtable htTokenInfo = null;
        if (Application["UserTokenInfo"] != null)
            htTokenInfo = (Hashtable)Application["UserTokenInfo"];
        //来源的Url(此Http请求上一个请求,由上一个请求使用参数传递)
        var returnUrl = string.Empty;
        if (Request["ReturnUrl"] != null)
            returnUrl = HttpUtility.HtmlDecode(Request["ReturnUrl"].Trim());
        string token = Request["Token"];
        if (htTokenInfo == null || string.IsNullOrEmpty(token))
        {
             //没有登录,跳转到登录页面
            Response.Redirect("SignIn.aspx?ReturnUrl=" + HttpUtility.UrlEncode(returnUrl));
        }
        else
        {
            //用户信息是否和Session关联
            if (htTokenInfo[token] == null)
                //没有登录,跳转到登录页面
                Response.Redirect("SignIn.aspx?ReturnUrl=" + HttpUtility.UrlEncode(returnUrl));
            //附加上Token参数
            if (returnUrl.IndexOf("?") > 0)
                returnUrl = returnUrl + "&UserName=" + htTokenInfo[token];
            else
                returnUrl = returnUrl + "?UserName=" + htTokenInfo[token];
            //跳回原页面
            Response.Redirect(returnUrl);
        }
}

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

protected void Page_Load(object sender, EventArgs e)
    {
        //清空Session
        Session.Clear();
        var returnUrl = string.Empty;
        if (Request["ReturnUrl"] != null)
            returnUrl = HttpUtility.HtmlDecode(Request["ReturnUrl"].Trim());
        //跳转到登录页面
        Response.Redirect("SignIn.aspx?ReturnUrl=" + HttpUtility.UrlEncode(returnUrl)); 
 }
代码下载:http://www.cnfounder.com/mw/UserCenterDemo.rar
欢迎转载,请注明出处。谢谢。

分布式系统用户统一认证浅析(一)--认证中心被动认证实现相关推荐

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

    上一篇文章写了被动认证的实现,本文接着写主动认证的实现. 一.认证流程 为了和被动认证有个比较,这些列出被动认证的流程图 1.1认证(图1为被动认证,图2为主动认证): 主动认证的流程和被动的区别是, ...

  2. @ZBBIX集成LDAP功能实现用户统一登录认证

    文章目录 1.zabbix认证方式 2.LDAP模块查看 3.windows AD配置 4.zabbix配置LDAP 5.开启LDAP账户登录 6.AD账户登录测试 7.AD账户批量添加zabbix ...

  3. ur机械臂 控制器_OnRobot末端执行器和统一接口已通过UR +计划认证

    近日,OnRobot 宣布其One System Solution末端执行器和统一接口现已通过UR +计划认证,UR +计划对夹具等配件进行测试和认证,以便与Universal Robots A / ...

  4. Zabbix 对接 LDAP 实现用户统一登录的方法

    需求 某公司环境是基于AD域来批量管理域用户的,zabbix监控平台上又要创建账号,这样非常麻烦,也不利于账号的管理,所以为了集中管理,想通过zabbix对接公司AD域用户,实现用户认证统一登录. 环 ...

  5. SpringBoot实现用户统一管理与单点登陆

    前言 最近在开发产品的过程中,需要将业务功能拆分成独立子系统,既可以单独使用也可以集成部署,这里就需要对框架进行扩展,支持用户统一管理与单点登陆.我们的基础框架使用redis实现token认证,所以只 ...

  6. 浅析 Spring Security 的认证过程及相关过滤器

    前言 上一篇文章 浅析 Spring Security 核心组件 中介绍了Spring Security的基本组件,有了前面的基础,这篇文章就来详细分析下Spring Security的认证过程. S ...

  7. 华为外部Portal认证 Radius认证计费 实现基于Mac快速认证的Mac无感知认证和结合CAS单点登录统一认证平台和AD域LDAP对接配置

    华为外部Portal认证 Radius认证计费 实现基于Mac快速认证的Mac无感知认证 结合CAS单点登录统一认证平台 AD域LDAP对接配置 实现用户名密码实名认证 访客短信认证 二维码扫码 钉钉 ...

  8. 用户统一密码管理校验服务说明

    1 背景说明 目前大多数企业尤其是集团性企业已经构建了一定的信息化系统,但伴随信息系统越建越多,账户体系紊乱.用户不统一.体系不统一的弊端越来越明显.当前企业亟需解决全集团内部的"统一认证& ...

  9. 配置用户通过Telnet登录设备的身份认证(AAA本地认证)

    背景信息 用户通过Telnet登录设备时,设备上必须配置验证方式,否则用户无法成功登录设备.设备支持不认证.密码认证和AAA认证三种用户界面的验证方式,其中AAA认证方式安全性最高. 采用AAA本地认 ...

最新文章

  1. 只知道用它打印了Hello World,除此之外你了解多少呢?
  2. 利用 Android Studio 和 Gradle 打包多版本APK
  3. Jupyter notebook 运行环境创建和切换 (Win10+Anaconda)
  4. 18、Page Object 设计模式
  5. PS制作立体效果——圆柱
  6. 【桶】220.存在重复元素 III 【LeetCode】
  7. css3动画animation,transition
  8. tf.nn.dropout
  9. 洛谷P2312解方程题解
  10. linux socket通信编程之c语言(客户端和服务器程序)
  11. Nginx常用命令介绍
  12. YQMKPAT(CAD图案填充插件)v2.1绿色版
  13. 【信号与系统】笔记合集,你确定不收藏吗?我已经收藏了
  14. 超定方程组最小二乘matlab,超定方程组最优解(最小二乘解)推导
  15. win32 指令大全
  16. 不浪漫爱情--等你来--等你来
  17. SparkSql之电影案例SQL编写
  18. ups不间断电源品牌_德国阳光蓄电池_蓄电池代理-山东万仁电源设备有限公司
  19. Adapter适配器
  20. 家用智能洗地机哪个牌子好、这几款旗舰机好用又实惠

热门文章

  1. 最简单易懂的24点解法(Java实现)
  2. 高质量子程序2——高内聚性
  3. 微信小程序 自定义带金额的日历组件
  4. HTML5应用性能调优工具WAPA – 安装篇
  5. CCS error: symbol trace buf is defined multiple times
  6. 全景制作软件哪款好?2020全景图片制作软件推荐!
  7. python zipfile_python zipfile模块学习笔记(一)
  8. 【汇智学堂】-python小游戏(太空阻击之三-场景创建)
  9. 自制U盘重装Win7系统
  10. 飞思卡尔MC9S12X PIT模块