【转】ASP.NET 表单验证实现浅析

对于Web应用的表单身份验证,因为公司有一个类库,采用 Session 实现,所以一直都没有去仔细了解。其实我并不赞成在 .NET 中用 Session 实现身份验证,毕竟 .NET 提供了一个强大的身份验证体系,并且公司的类库也没有实现什么特殊的功能,仅只是保存一个 Session 变量来提供身份识别,在安全性和可用性上与 .NET 的实现相比,个人感觉还是有较大的差距。

近期很少加班,就抽空看了一下,理了个大致的思路出来。

首先,自然是配置 Web.config,在 <system.web> 下设定:

 <authentication mode="Forms">

<forms name=".SomeTsteAuth"

loginUrl="admin/login.aspx"

defaultUrl="admin/index.aspx"

path="/"

timeout="10">

</forms>

</authentication>

<authentication> 的 Mode=”Forms” 指定 Web 应用采用表单验证,另外的方式还有“Windows”、“Passport”和“None”,“Windows”常用在局域网中,配合 AD 进行身份验证,“Passport”好像要交钱给微软后才能够使用,不太清楚了。“None”表示不进行验证。

<forms> 的几个常用属性:

name 属性指定验证所需要的 cookie 的名称,默认值是“.ASPXAUTH”,如果在一个服务器上下挂了多个 Web 应用程序,必须重新指定该名称,因为每个应用程序都需要唯一的 cookie。

loginUrl 属性指定登录用的页面,用于提供用户名和密码,默认值是“login.aspx”。该页面可以和需要提供身份验证才能访问的页面放在同一个目录下(呵,我原以为这个页面要放在单独的可公开访问的目录下)。

defaultUrl 属性指定登入后跳转到的页面,默认值是“default.aspx”,当然你也可以跳转到用户登入前的前一个页面,并且这是 .NET 的默认实现。

path 属性指定 cookie 的路径,默认值为“/”,对于大多数浏览器而言,cookie 的 path 是区分大小写的,因此如果路径的大小写不符,将不会发回 cookie。(注意:“/”指网站的根目录,在开发时,Visual studio 通常会在网站根目录下建立一个新的目录作为 Web 应用程序的根,例如http://localhost/MySite,此时若你要单独针对 Web 应用程序设置 path,必须从网站根目录后的路径指定,例如,要设定刚才的 Web 应用程序访问 Admin 目录下的文件需要身份验证时,path 值应为“MySite/Admin”,而不是“Admin”,否则用户将无法正常登入。)

timeout 属性指定用户多长时间不进行操作,身份凭证会过期,以分钟为单位,默认为 30 分钟。

设定好 <authentication> 以后,还需要设置 <authorication>,最常见的方式如下:

                <authorization>            

            <deny users="?"/>  

        </authorization>

“?”号表示匿名用户,“*”号表示所有用户。在网上看到一些文档,在<deny users=”?” /> 下面还有一句 <allow users=”*” />,其实没必要增加这一句,除非使用角色对各个子目录分别进行权限控制。因为这样有一个潜在的危险,如果有人不小心把 <allow users=”*” /> 放在了 <deny user=”?” /> 前面,系统将不会进行验证。

如果要对子目录进行分别的权限管控,需要新增一个 <location> 段到 Web.config 的根元素 <configuration> 下:

<location path="admin">

     <system.web>

         <authorization>

             <deny users="?"/>

         </authorization>

     </system.web>

</location>

如果涉及到多个子目录,并要分配不同的权限,那就需要使用角色。将 <authorization> 下的内容换成

  <allow roles="Admin"/>

  <deny users="*"/>

Web.config 的配置大致如此,需要注意的一点是 <authentication> 节一个 Web 应用程序只能有一个,即我们在一个 Web 应用程序中只能采用一种验证方式,而 <location> 节可以配置多个,以对各目录进行不同的权限管控。对于未配置 <authorization> 节的目录,访问权限与 Web 应用程序根相同,若应用程序的根也未配置 <authorization>,则默认为任何人可访问,即使 <authentication> 的 mode 属性设置为“Forms”。

       

其次就是编码,在登录页面的“登录”按钮点击事件中:

        if (UserName.Text.Trim() == "你的用户名" && Password.Text == "你的密码")

            FormsAuthentication.RedirectFromLoginPage("你的用户名", false);

    else

        //提示用户名和密码不正确

RedirectFromLoginPage() 的第一个参数是目前正在验证的用户名,第二个参数指是否长期保存登录信息到 cookie 中,这个参数的意义在 .NET 2.0 中与 .NET 1.1 中不同,在 .NET 1.1 中,会将登录信息保存到 cookie 中,并设定过期时间为 50 年后,即你以后再也不用输入用户名和密码,除非 cookie 被删除或 50 年后(老眼昏花的你加上一台堪称古董的电脑,最美不过夕阳红)。在 .NET 2.0 中,这个参数仅指在关闭浏览器后,登录信息在 cookie 中是否还存在,而过期时间的约束依然有效,即 cookie 过期后,无论你重启浏览器与否,仍需输入登录凭证。

代码简单得出乎意料,.NET 会自动创建票劵并重定向到登录前用户访问的那个需要进行验证的页面,若用户直接访问登录页面,则重定向到 Web.config 中定义的 default 页面。如果需要自己控制重定向的过程,可以这么做:

        if (UserName.Text.Trim() == "你的用户名" && Password.Text == "你的密码")

    {

        FormsAuthentication.SetAuthCookie("你的用户名", false);

        Response.Redirect("Index.aspx");

    }

    else

        //提示用户名和密码不正确

其实,语句 FormsAuthentication.RedirectFromLoginPage("你的用户名", false) 等同于

FormsAuthentication.SetAuthCookie("你的用户名", false);

Response.Redirect(FormsAuthentication.GetRedirectUrl("你的用户名", false));

当然,也可以手动创建票劵,并加入到响应的 cookie 集合中,完整的代码如下:

    if (UserName.Text.Trim() == "你的用户名" && Password.Text == "你的密码")

    {

            //为当前登录用户创建一个新的票劵

        FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(

2, //版本号

"你的用户名", //登录的用户名

DateTime.Now, //票劵发布时间

DateTime.Now.AddMinutes(15), //票劵过期时间

false, //是否在关闭浏览器后仍然保留登录信息

"", //可加入少量用户数据(注意:不能为 null)

FormsAuthentication.FormsCookiePath //cookie 路径

);

        //加密票劵,获取加密后的字串

        string encrypt = FormsAuthentication.Encrypt(ticket);

        //使用加密后的字串建立一个 cookie

        HttpCookie cookie = new HttpCookie(

FormsAuthentication.FormsCookieName,

Encrypt

);     

        //将 cookie 增加到客户端

        Response.Cookies.Add(cookie);

        Response.Redirect(FormsAuthentication.GetRedirectUrl("你的用户名", false));

    }

    else

        //提示用户名和密码不正确

 

到这里,应该很清楚了,表单验证,其实质是使用一个特定的 cookie,在每次连接服务器时验证该 cookie 是否存在,从而决定用户是否具有相应的权限。在上述代码中,也可以增加对 cookie 控制的代码,在使用加密后的票劵建立一个 cookie 后,增加代码:

    //HttpOnly 属性为 true,表示该 cookie 不能在浏览器端进行存取

    cookie.HttpOnly = true;

    //cookie 的路径,取 Web.config 中 <forms> 属性 path 的值

    cookie.Path = FormsAuthentication.FormsCookiePath;

    //设置 cookie 的过期时间,与票劵的过期时间一致,如果这两个时间不一致,则其中任何一个时间到期时,均视为过期           

    cookie.Expires = ticket.Expiration;

 

清楚表单验证的大致机制后,对于基于角色的表单验证也是手到擒来,大致的过程叙述如下:

1 在登录页面的代码文件中,新增一个普通的 cookie,将用户所属的角色保存到该 cookie 中(一个用户可具备多个角色)。

2 在 Global.asax 的 AuthenticateRequest 事件中,判断用户是否已通过验证,若已通过验证,则从1增加的 cookie 中取出角色字符串,并构建一个 System.Security.Principal.GenericPrincipal 对象,该对象的构造函数包括两个参数:用户标识和角色数组,用户标识可通过 HttpContext.Current.User.Identity 取得,角色数组将角色字符串转换为字符串数组赋进去即可。

3 调整 Web.config 设置角色的权限。

posted @ 2011-11-22 15:48  明之道  阅读(244)  评论(1编辑  收藏  举报