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

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 配置,相关方法用来操作身份验证数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值