[.NET 基于角色安全性验证] 之二:ASP.NET Forms 身份验证流程分析

转载 2008年10月01日 10:55:00
 MSDN 中提及 FormsAuthenticationModule 在 Forms 身份验证中起到了关键作用,那么这背后究竟隐藏了什么?本分将简要分析 Forms 身份验证流程,以便让大家更加清楚地了解并使用它。

FormsAuthenticationModule 是一个 Http Module,Forms 身份验证通过 FormsAuthenticationModule 参与 ASP.NET 页的生命周期。它在网站应用启动时被初始化,并拦截访问请求,我们继续看看它的细节。
public void Init(HttpApplication app)
{
  // 绑定 HttpApplication 的两个事件,以此来拦截系统的访问请求。
  app.AuthenticateRequest += new EventHandler(this.OnEnter);
  app.EndRequest += new EventHandler(this.OnLeave);
}

private void OnEnter(object source, EventArgs eventArgs)
{
  if (!FormsAuthenticationModule._fAuthChecked || FormsAuthenticationModule._fAuthRequired)
  {
    HttpApplication application1 = (HttpApplication)source;
    HttpContext context1 = application1.Context;

    // 读取 Forms 认证的配置信息
    AuthenticationSection section1 = RuntimeConfig.GetAppConfig().Authentication;
    section1.ValidateAuthenticationMode();

    // 确认 Forms 验证模式
    if (!FormsAuthenticationModule._fAuthChecked)
    {
      FormsAuthenticationModule._fAuthRequired = section1.Mode == AuthenticationMode.Forms;
      FormsAuthenticationModule._fAuthChecked = true;
    }

    if (FormsAuthenticationModule._fAuthRequired)
    {
      // 设置缺省参数
      if (!this._fFormsInit)
      {
        FormsAuthentication.Initialize();
        this._FormsName = section1.Forms.Name;
        if (this._FormsName == null)
        {
          this._FormsName = ".ASPXAUTH";
        }
        FormsAuthenticationModule.Trace("Forms name is: " + this._FormsName);
        this._LoginUrl = section1.Forms.LoginUrl;
        if (this._LoginUrl == null)
        {
          this._LoginUrl = "login.aspx";
        }
        this._fFormsInit = true;
      }

      // 调用验证核心方法
      this.OnAuthenticate(new FormsAuthenticationEventArgs(context1));

      CookielessHelperClass class1 = context1.CookielessHelper;
      if (AuthenticationConfig.AccessingLoginPage(context1, this._LoginUrl))
      {
        context1._skipAuthorization = true;
        class1.RedirectWithDetectionIfRequired(null, FormsAuthentication.CookieMode);
      }
      if (!context1.SkipAuthorization)
      {
        context1._skipAuthorization = AssemblyResourceLoader.IsValidWebResourceRequest(context1);
      }
    }
  }
}

private void OnAuthenticate(FormsAuthenticationEventArgs e)
{
  HttpCookie cookie1 = null;
  if (this._eventHandler != null)
  {
    this._eventHandler(this, e);
  }

  // 检查 User 对象,以确认是否已通过验证。
  if ((e.Context.User != null) || (e.User != null))
  {
    // 处理 Global.asax FormsAuthentication_OnAuthenticate 事件参数。
    if (e.Context.User == null)
    {
      e.Context._user = e.User;
    }
  }
  else
  {
    FormsAuthenticationTicket ticket1 = null;
    bool flag1 = false;
    try
    {
      // 从 Cookie 中提取验证票证对象
      ticket1 = FormsAuthenticationModule.ExtractTicketFromCookie(e.Context, this._FormsName, out flag1);
    }
    catch
    {
      ticket1 = null;
    }

    if ((ticket1 != null) && !ticket1.Expired)
    {
      FormsAuthenticationTicket ticket2 = ticket1;

      // 刷新票证信息
      if (FormsAuthentication.SlidingExpiration)
      {
        ticket2 = FormsAuthentication.RenewTicketIfOld(ticket1);
      }

      // 创建主体和标识对象
      e.Context._user = new GenericPrincipal(new FormsIdentity(ticket2), new string[0]);

      if (!flag1 && !ticket2.CookiePath.Equals("/"))
      {
        cookie1 = e.Context.Request.Cookies[this._FormsName];
        if (cookie1 != null)
        {
          cookie1.Path = ticket2.CookiePath;
        }
      }
      if (ticket2 != ticket1)
      {
        if ((flag1 && (ticket2.CookiePath != "/")) && (ticket2.CookiePath.Length > 1))
        {
          ticket2 = new FormsAuthenticationTicket(ticket2.Version, ticket2.Name, ticket2.IssueDate, ticket2.Expiration, ticket2.IsPersistent, ticket2.UserData, "/");
        }

        // 加密新的票证,并写入 Cookie。
        string text1 = FormsAuthentication.Encrypt(ticket2);
        if (flag1)
        {
          e.Context.CookielessHelper.SetCookieValue('F', text1);
          e.Context.Response.Redirect(e.Context.Request.PathWithQueryString);
        }
        else
        {
          if (cookie1 != null)
          {
            cookie1 = e.Context.Request.Cookies[this._FormsName];
          }
          if (cookie1 == null)
          {
            cookie1 = new HttpCookie(this._FormsName, text1);
            cookie1.Path = ticket2.CookiePath;
          }
          if (ticket2.IsPersistent)
          {
            cookie1.Expires = ticket2.Expiration;
          }
          cookie1.Value = text1;
          cookie1.Secure = FormsAuthentication.RequireSSL;
          cookie1.HttpOnly = true;
          if (FormsAuthentication.CookieDomain != null)
          {
            cookie1.Domain = FormsAuthentication.CookieDomain;
          }
          e.Context.Response.Cookies.Add(cookie1);
        }
      }
    }
  }
}

private void OnLeave(object source, EventArgs eventArgs)
{
  // 调整 URL
  if (FormsAuthenticationModule._fAuthChecked && FormsAuthenticationModule._fAuthRequired)
  {
    HttpApplication application1 = (HttpApplication)source;
    HttpContext context1 = application1.Context;
    if (context1.Response.StatusCode == 0x191)
    {
      string text3;
      string text1 = null;
      if (!string.IsNullOrEmpty(this._LoginUrl))
      {
        text1 = AuthenticationConfig.GetCompleteLoginUrl(context1, this._LoginUrl);
      }
      if ((text1 == null) || (text1.Length <= 0))
      {
        throw new HttpException(SR.GetString("Auth_Invalid_Login_Url"));
      }
      CookielessHelperClass class1 = context1.CookielessHelper;
      string text2 = context1.Request.PathWithQueryString;
      if (text1.IndexOf('?') >= 0)
      {
        text1 = FormsAuthentication.RemoveQueryStringVariableFromUrl(text1, "ReturnUrl");
        text3 = text1 + "&ReturnUrl=" + HttpUtility.UrlEncode(text2, context1.Request.ContentEncoding);
      }
      else
      {
        text3 = text1 + "?ReturnUrl=" + HttpUtility.UrlEncode(text2, context1.Request.ContentEncoding);
      }
      int num1 = text2.IndexOf('?');
      if ((num1 >= 0) && (num1 < (text2.Length - 1)))
      {
        text2 = FormsAuthentication.RemoveQueryStringVariableFromUrl(text2, "ReturnUrl");
      }
      num1 = text2.IndexOf('?');
      if ((num1 >= 0) && (num1 < (text2.Length - 1)))
      {
        text3 = text3 + "&" + text2.Substring(num1 + 1);
      }
      class1.SetCookieValue('F', null);
      class1.RedirectWithDetectionIfRequired(text3, FormsAuthentication.CookieMode);
      context1.Response.Redirect(text3, false);
    }
  }
}

通过对 FormsAuthenticationModule 代码的分析,我们基本可以确定 ASP.NET Forms 身份验证流程。

1. 拦截系统访问,检查身份验证状态。
2. 如未通过验证,则跳转到指定的 loginUrl 接受用户身份数据验证。
3. 如已通过验证,则从 Cookie 中提取身份验证票证对象。
4. 创建用户标识和主体对象,其中标识对象包含了票证引用。
5. 更新票证过期时间,重新写入 Cookie。
6. 调整URL参数,重定向页面。

在整个验证流程中,我们可以看到几个验证的核心类型。包括:

FormsAuthenticationTicket :身份验证票证,保存用户名、过期时间、自定义数据等信息。经 FormsAuthentication.Encrypt 方法加密成字符串后保存到 Cookie 或者 URL 参数中。

GenericPrincipal :用户主体对象。HttpContext.User 就是该类型,用来保存用户身份数据,诸如:FormsIdentity、FormsAuthenticationTicket 等。

FormsIdentity:用户标识对象,可通过 HttpContext.Current.User.Identity 访问。其 Ticket 属性保存了用户身份验证票据引用。

HttpContext:ASP.NET 为每个用户创建的上下文对象,用来保存该用户的相关信息,诸如 Session、GenericPrincipal 等。

FormsAuthentication:ASP.NET Forms 身份验证的核心类之一,其静态属性可访问 Forms Web.config 配置,相关方法用来操作身份验证数据。

ASP.NET MVC:窗体身份验证及角色权限管理示例

前言   本来使用Forms Authentication进行用户验证的方式是最常见的,但系统地阐明其方法的文章并不多见,网上更多的文章都是介绍其中某一部分的使用方法或实现原理,而更多的朋友则发文询...
  • hpnets
  • hpnets
  • 2015年03月07日 18:20
  • 4735

Asp.Net MVC中身份认证和授权

 http://blog.csdn.net/kenshincui/article/details/5559508 MVC自带的ActionFilter 在Asp.Net WebFor...
  • Lyncai
  • Lyncai
  • 2015年03月16日 21:09
  • 3150

转:ASP.NET MVC:窗体身份验证及角色权限管理示例

前言   本来使用Forms Authentication进行用户验证的方式是最常见的,但系统地阐明其方法的文章并不多见,网上更多的文章都是介绍其中某一部分的使用方法或实现原理,而更多的朋友则发...
  • lenovouser
  • lenovouser
  • 2016年08月09日 22:47
  • 1162

[.NET 基于角色安全性验证] 之三:ASP.NET Forms 身份验证

[ 2006-08-08 11:11:56 | 作者: yuhen ] 字号: 大 | 中 | 小 在开发过程中,我们需要做的事情包括: 1. 在 web.config 中设...
  • wuhan2010
  • wuhan2010
  • 2011年11月29日 15:58
  • 145

asp.net登录 用Forms身份验证和基于角色的分目录访问

Forms身份验证用来判断是否合法用户,当用户合法后,再通过用户的角色决定能访问的页面。主要思想:Forms身份验证用来判断是否合法用户,当用户合法后,再通过用户的角色决定能访问的页面。 具体步骤: ...
  • bianjing40
  • bianjing40
  • 2015年05月31日 15:30
  • 445

asp.net实战Forms身份验证/角色案例

  • 2009年09月14日 15:17
  • 143KB
  • 下载

Asp.net Forms身份验证

前言:     因为自己一直从事企业内部系统开发,一直将登陆用户信息保存在Session中.没有用过什么Forms身份验证,最近研究了一下这方面的东西,贴一下,欢迎大家拍砖. Froms身份验证:...
  • softkexin
  • softkexin
  • 2012年05月11日 11:43
  • 2775

asp.net Forms身份验证详解

在做网站的时候,都会用到用户登录的功能。对于一些敏感的资源,我们只希望被授权的用户才能够访问,这让然需要用户的身份验证。对于初学者,通常将用户登录信息存放在Session中,笔者在刚接触到asp.ne...
  • yin554393109
  • yin554393109
  • 2012年05月25日 16:40
  • 149

asp.net Forms表单验证(角色) 使用经验及验证流程分析

asp.net Forms表单验证 使用经验及验证流程分析       最近,要做一个登陆的页面,就想到了安全性方面的问题。记得曾经在邵志东老师讲的关于asp.net安全性方面的课程中,...
  • y250915790
  • y250915790
  • 2013年05月18日 13:51
  • 654

实例ASP.NET基于表单的角色身份验证

  • 2017年04月22日 20:24
  • 6.79MB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:[.NET 基于角色安全性验证] 之二:ASP.NET Forms 身份验证流程分析
举报原因:
原因补充:

(最多只允许输入30个字)