创建网站中,常常会使用到身份验证。asp.net中内置了几种身份验证的方式,如Windows、Froms、Passport等。这几种身份验证的方式各有不同。一般来说,网站的身份验证方式都会经过以下几个步骤:
1、输入用户名和密码,单击确定按钮。
2、在后台判断用户名和密码是否正确,如果错误返回提示;如果正确,进入可访问的页面。
在ASP时代,通常都会在验证用户名和密码是否匹配之后,创建一个Session,然后在每个需要验证的页面中判断Session是否存在,如果存在,则显示页面内容;如果不存在,产生提示,并跳转到登录页面。
但是,在asp.net时代,这个过程就给大大的减化了,不再需要在每个需要验证的页面中去校验Session,只需要进行如下几步,就可以完成身份验证过程。
第一步:修改web.config文件。
1、在<system.web>和</system.web>中找到<authentication>节,将其改为“<authentication mode="Forms" />”,其中Forms代表使用表单认证。
2、<system.web>和</system.web>中添加“<authorization><deny users="?"/></authorization>”,其中“<deny users="?"/>”代表拒绝所有的匿名用户。
第二步:创建login.aspx文件。
在经过第一步之后,无论用户访问网站中的哪个文件,只要没有经过身份验证,asp.net会自动跳转到login.aspx网页上,并且在URL中使用ReturnUrl参数来传递用户当前访问的网页。
假设用户没有经过身份验证就直接访问test.aspx文件,那么asp.net会自动跳转了login.aspx网页,此时浏览器窗口中的地址栏中的URL为:“login.aspx?ReturnUrl=%2ftest.aspx”,因此,可以在身份验证通过后,再将网页跳回到ReturnUrl参数指定的网页上去。
第三步:在login.aspx文件中验证身份。
身份验证方式比较简单,一般都是创建一个文本框和一个密码框,用户输入用户名和密码后,单击提交按钮,则去数据库中验证身份,详细过程就不写了,在此只要输入的用户名为1,密码为2就认为身份验证通过。
身份验证完毕之后,使用FormsAuthentication.SetAuthCookie()为用户创建一个身份验证的票据,并将其添加到Cookie中。以后,再访问网站中的其他网页,就不需要使用进行身份验证了。单击提交按钮后的代码如下所示。
[c-sharp] view plain copy
- protected void Button1_Click(object sender, EventArgs e)
- {
- //身份验证方式,本例中用户名为1,密码为2
- if (TextBox1.Text == "1" && TextBox2.Text == "2")
- {
- FormsAuthentication.SetAuthCookie(TextBox1.Text, false);
- }
- //如果URL中没有传递ReturnUrl参数,则跳转到Default.aspx,否则跳转到ReturnUrl参数值指定的网页
- if (string.IsNullOrEmpty(Request.QueryString["ReturnUrl"]))
- {
- Response.Redirect("Default.aspx");
- }
- else
- {
- Response.Redirect(Request.QueryString["ReturnUrl"].ToString());
- }
- }
-
在上一篇博文《asp.net中的窗体身份验证(最简单篇)》中的身份验证虽然很简单,但是有一个缺点,就是访问整个网站都必须要经过身份验证,而事实上,很多网站都不会这么要求的。
比如一个新闻系统,通常只有在发布新闻的网页才需要身份验证,而用户浏览新闻是不需要身份验证的。对于这种情况,就要针对不同的网页来进行身份验证了。
要实现这种功能,也只要将《asp.net中的窗体身份验证(最简单篇)》稍作修改,具体操作方式如下所示:
第一步、创建一个子文件夹,将所有要验证的网页都放在这个文件夹中,设置该文件夹的名字为“admin”。
第二步、修改web.config文件。
1、在<system.web>和</system.web>中找到<authentication>节,将其改为“<authentication mode="Forms"><forms loginUrl="~/admin/AdminLogin.aspx"></forms></authentication>”,其中Forms代表使用表单认证;loginUrl用于指定登录页面URL,个人比较喜欢将登录页面也放在admin文件夹中,所以在此要指定登录页面URL,如果还是想使用根目录中的login.aspx页面作为登录页面,则可以省略<forms>节。
2、在<system.web>和</system.web>中添加“<authorization><allow users="?"/></authorization>”,其中“<allow users="?"/>”代表允许所有的匿名用户。注意此处与《asp.net中的窗体身份验证(最简单篇)》的区别,在《asp.net中的窗体身份验证(最简单篇)》中,此处使用的是“<deny users="?"/>”,用于拒绝所有匿名用户。
第三步、在需要身份验证的子文件夹中添加一个web.config文件,在本例中为在“admin”文件夹中添加该文件。
第四步、修改需要身份验证的子文件夹中的web.config文件,在本例中为“admin”文件夹中的web.config文件。
在<system.web>和</system.web>中,添加“<authorization><deny users="?"/></authorization>”,由于在根目录中的web.config文件中声明了允许所有匿名用户访问,所以在不能允许匿名用户访问的子文件夹中,必须要使用“<deny users="?"/>”来拒绝匿名用户访问。另外,在子文件夹中的web.config文件中可以没有<authentication>节。
第五步、在“admin”子文件夹中创建AdminLogin.aspx文件。如果在第二步中没有使用<forms>节来指定用户登录页面,则在网站根目录下创建login.aspx文件。
第六步、在AdminLogin.aspx文件(或login.aspx文件)中验证身份,如果身份验证通过,使用FormsAuthentication.SetAuthCookie()为用户创建一个身份验证的票据,并将其添加到Cookie中。以后,再访问网站中admin子目录下的其他网页,就不需要使用进行身份验证了。单击提交按钮后的代码与《asp.net中的窗体身份验证(最简单篇)》中的代码类似,在此就不再赘述了。
在《asp.net中的窗体身份验证(最简单篇) 》中介绍了使用FormsAuthentication.SetAuthCookie()方法创建身份验证票据的方法,事实上,这是一个使用缺省的身份验证票据的方法。在asp.net中,Forms身份验证的方式是在用户登录时创建一个身份验证票,然后将这个身份验证票存放在Cookie中,以后整个网站都可以通过这个Cookie来判断用户是否已经登录。如果用户浏览器不支持Cookie,asp.net也可以将票证放在URL的查询字符串中进行传递,这个不是本文的 重点,也就不详细介绍了。
那么当访问一个asp.net的网站时,asp.net究竟是怎么进行身份验证的呢?
在asp.net中,将身份验证分成了两个部分,第一个部分是IIS的身份验证,在用户访问网站时,IIS首先就会对用户进行身份验证,这个身份验证的具体设置在IIS中,这也非本文的重点,在此也不再详细介绍了。只有IIS通过了用户的身份验证之后,才会进行第二个部分的身份验证,这个部分的身份验证则由asp.net来完成。
asp.net的身份验证方式由web.config文件中的<authentication>节点的mode属性值设置,如果要使用Forms身份验证,mode属性值必须为Forms。
设置完<authentication>节点的mode属性值之后,我们还可以在该节点下添加一个<forms>节点,用于说明Forms身份验证的具体选项。常用的<authentication>节点的设置方式如下所示:
[c-sharp] view plain copy
- <authentication mode="Forms">
- <forms
- loginUrl="AdminLogin.aspx"
- timeout="30"
- name=".ASPXAUTH"
- path="/"
- requireSSL="false"
- cookieless="UseDeviceProfile"
- defaultUrl="default.aspx"
- slidingExpiration="true"
- protection="All"
- enableCrossAppRedirects="false">
- </forms>
- </authentication>
以上代码中,loginUrl为用户登录网页,如果省略,asp.net将使用网站根目录下的login.aspx为登录页面。timeout设置登录超时时间为30分钟。name为存储身份验证票据的Cookie名,默认值为“.ASPXAUTH” 。path为存储身份验证票据的Cookie的路径,默认值为“/”。requireSSL为存储身份验证票据的Cookie是否使用SSL加密传输,默认为false。cookieless为浏览器不支持Cookie时的存储身份验证票据的传递方式,默认值为“UseDeviceProfile”,即自动检测浏览器是否支持Cookie,如果浏览器支持Cookie则使用Cookie传递身份验证票据,如果浏览器不支持Cookie则使用URL传递身份验证票据。defaultUrl为登录后默认跳转的网页,默认值为“default.aspx”。slidingExpiration为是否以执行可变的会话生存期,默认值为true。protection为Cookie的加密类型,默认值为“All”,即对Cookie同时使用数据验证和加密方法,其中数据验证算法由<machineKey>节点中设置。enableCrossAppRedirects是否将通过身份验证的用户重新定向到其它Web应用程序的URL中,默认值为false。
在以上代码中,<forms>节点中的protection属性值为All,说明要对Cookie同时使用数据验证和加密方法,而数据验证算法由<machineKey>节点中设置,这就意味着要在<system.web>节点下添加一个<machineKey>子节点。<machineKey>节点的作用是对密钥进行设置,如以下代码所示:
[c-sharp] view plain copy
- <machineKey validation="3DES"/>
<authentication>节点用于设置asp.net的身份验证方式,也就是要怎么去验证用户身份,但验证完用户身份之后,哪些用户可以访问资源,<authentication>节点就不能进行设置了,这个使用就 必须要使用到<authorization>节点,该节点可以设置应用程序的授权,只有授权的用户才能访问网站资源。
<authorization>节点下面可以有两种子节点:<allow>和<deny>。其中<allow>节点用于说明允许对网站资源访问的规则。<deny>节点用于说明禁止对网站资源访问的规则。
拒绝匿名用户访问的设置方式如下所示:
[c-sharp] view plain copy
- <authorization>
- <deny users="?"/>
- </authorization>>
经过以上几个步骤,web.config文件的修改基本上就结束了,一个完整的web.config文件如下所示:
[c-sharp] view plain copy
- <?xml version="1.0" encoding="utf-8"?>
- <configuration>
- <appSettings/>
- <connectionStrings/>
- <system.web>
- <compilation debug="true" />
- <authentication mode="Forms">
- <forms
- loginUrl="AdminLogin.aspx"
- timeout="30"
- name=".ASPXAUTH"
- path="/"
- requireSSL="false"
- cookieless="UseDeviceProfile"
- defaultUrl="default.aspx"
- slidingExpiration="true"
- protection="All"
- enableCrossAppRedirects="false">
- </forms>
- </authentication>
- <authorization>
- <deny users="?"/>
- </authorization>
- <machineKey validation="3DES"/>
- </system.web>
- </configuration>
至此为止,网站窗体身份验证方式就已经创建完毕,此时,无法访问网站中的哪个网页,都会自动跳转到AdminLogin.aspx页面,下一步可以编写AdminLogin.aspx网页代码了。
本文中涉及到的web.confing文件中的节点的详细解释可以在《asp.net中的窗体身份验证(完整篇之附录:web.config中相应节点详解)》中查阅。
在《asp.net中的窗体身份验证(完整篇之一:创建asp.net的窗体身份验证方式) 》中介绍了如何通过修改web.config文件来创建窗体身份验证。修改完web.config文件之后,网站就可以使用窗体身份验证方式来验证用户身份了。那么,整个身份验证的过程是怎么样的呢?
前面说过,在asp.net中,身份验证过程分为两部分,一部分是IIS中的身份验证,只有通过了IIS中的身份验证之后,才行进行asp.net中的身份验证。一个完整的窗体身份验证流程如下所示:
- 首先,用户通过浏览器向服务器发送一个网页请求,假设用户要访问index.aspx网页, 那么浏览器就会将请求发送给IIS服务器。
- 假设在该服务器中将IIS设置成可以匿名访问,那么IIS服务器将会允许访问index.aspx网页。
- IIS服务器允许访问之后,将会进入asp.net身份验证。asp.net会去web.config文件中查看<authorization>节点下的<deny>节点中是否拒绝所有匿名用户。如果拒绝了所有匿名用户,服务器则会去查找一个身份验证的Cookie。
-
如果服务器查找不到该身份验证的Cookie,就会将网页跳转到登录页面。这个登录页面为<forms>节点中的loginUrl属性值,默认为网站根目录下的login.aspx。在跳转到到登录页面时,服务器会将当前访问的网页的地址,如index.aspx,作为ReturnUrl参数值附加到login.aspx的URL中。如下所示:
login.aspx?ReturnUrl=%2findex.aspx
- 用户在登录页面输入用户名和密码(或别的登录信息),并将这些信息提交到服务器。
- 服务器接收到用户提交的信息之后,判断用户是否为合法用户(判断方式有很多种,但这不是本文的重点),如果用户为合法用户,则创建一个包含窗体身份验证票的Cookie。
- 在创建完包含窗体身份验证票的Cookie之后,可以使用代码将网页跳回登录前访问的页面,如index.aspx网页(这个时侯ReturnUrl参数就起作用了)。
- 在登录后访问其它网页时(如跳转后的index.aspx页面),服务器会检测包含窗体身份验证的Cookie,并对用户进行身份验证。身份验证成功后,服务器返回用户需要访问的网页。
以上就是一个完整的asp.net窗体身份验证流程,下面将会介绍如何按着该流程来编写asp.net代码。
在《asp.net中的窗体身份验证(完整篇之二:asp.net的窗体身份验证过程) 》中我们介绍了asp.net的窗体身份验证过程,按照这个流程,我们可以开始动手编写网页代码。根据《asp.net中的窗体身份验证(完整篇之一:创建asp.net的窗体身份验证方式) 》中的web.config代码,我们网站的登录页面为AdminLogin.aspx。在没有登录的情况下,如果访问Default.aspx网页,服务器会自动跳转到AdminLogin.aspx,如下图所示。
ASP.NET Forms验证详解 从上图中可以看出,浏览器地址栏中,AdminLogin.aspx后有参数ReturnUrl,而ReturnUrl的值跳转之间访问的Default.aspx网页。
在上图所示页面中,用户可以输入用户名和密码,然后单击登录按钮提交信息。在后台,服务器接收到用户提交的信息后,可以判断用户提交的用户和密码是否匹配,如果匹配,则认为身份验证通过。这个时候,进入了《asp.net中的窗体身份验证(完整篇之二:asp.net的窗体身份验证过程) 》中的第6步,也就是“创建一个包含窗体身份验证票的Cookie”。
所谓“创建一个包含窗体身份验证票的Cookie”,事实上是分两步走,第一步是创建一个窗体身份验证票,第二步是将身份验证票放在Cookie中。不过呢,在asp.net中,可以简化这个过程。
方式一:使用默认的身份验证票。这个时候单击登录按钮的代码如下所示:
[c-sharp] view plain copy
- protected void Button1_Click(object sender, EventArgs e)
- {
- Label1.Text = "";
- //当用户名为1,密码为2时,身份验证通过
- if (TextBox1.Text == "1" && TextBox2.Text == "2")
- {
- //方式一:使用默认的身份验证票,并跳转到初始请求的页面或默认页面
- FormsAuthentication.RedirectFromLoginPage("yundao", false);
- }
- else
- {
- Label1.Text = "用户名为1,密码为2,请重新输入";
- }
- }
- 创建身份验证票;
- 将身份验证票放在Cookie中;
- 跳转到登录前的网页或默认网页。
- 创建一个默认的身份验证票。
- 将该默认的身份验证票存放在默认的Cookie中。此时,用于存放身份验证票的Cookie名由web.config文件中的<forms>节点的name属性值决定,如果没有设置该属性,默认情况下,Cookie名为“.ASPXAUTH”。
- 跳转网页。跳转网页的地址由登录页面的URL中的ReturnUrl参数决定,如上图所示,登录后将跳转到default.aspx页面。如果登录页面的URL中没有ReturnUrl参数(也就是说,直接访问的登录页面),那么将会跳转到web.config文件中的<forms>节点的defaultUrl属性值所指定的页面,如果没有设置该属性,默认情况下为default.aspx页面。
RedirectFromLoginPage()方法中第一个参数为经过身份验证的用户名,这个用户名可以与用户的登录名相同,也可以不相同。第二个参数为是否创建永久的Cookie。通常为了安全起见,都不会创建永久的Cookie,因此该参数值通常为false。但如果创建的非永久的Cookie,那么在关闭浏览器窗口之后,再进入网站,就需要重新进行身份验证,而创建永久的Cookie,只要身份验证过之后,无论再打开该网站中的什么网页,都不会要求再一次登录。
本例源代码可以在http://www.aspxfans.com/myBlogFile/窗体身份验证:使用RedirectFromLoginPage方法验证身份.rar或http://download.csdn.net/source/1962420中下载。
以上方式是最简单的窗体身份验证方式,因为使用的是默认的身份验证票,但以上代码有一个缺点,就是身份验证通过后,要么跳转到登录页面前的网页,要么跳转到默认网页。如果想停留在当前网页,RedirectFromLoginPage()方法就无能为力了,在这种情况下,可以使用FormsAuthentication类的SetAuthCookie()方法,该方法的作用只是创建一个默认的身份验证票并将其写入Cookie中,但不跳转网页。如果要跳转网页的话,可以判断ReturnUrl参数值是否为空,再决定是否跳转网页。如以下代码所示:
[c-sharp] view plain copy
- protected void Button1_Click(object sender, EventArgs e)
- {
- Label1.Text = "";
- //当用户名为1,密码为2时,身份验证通过
- if (TextBox1.Text == "1" && TextBox2.Text == "2")
- {
- //方式二:使用默认的身份验证票,并写入Cookie
- FormsAuthentication.SetAuthCookie("yundao", false);
- //判断初始请求页是否为空
- if (string.IsNullOrEmpty(Request.QueryString["ReturnUrl"]))
- {
- Label1.Text = "登录成功,请选择以下网页:";
- }
- else
- {
- Response.Redirect(Request.QueryString["ReturnUrl"].Trim());
- }
- }
- else
- {
- Label1.Text = "用户名为1,密码为2,请重新输入";
- }
- }
从以上代码可以看出,SetAuthCookie()方法与RedirectFromLoginPage()方法相比,缺少了跳转的步骤。因此,可以更加灵活地掌握用户跳转的页面。SetAuthCookie()方法的第一个参数也是经过身份验证的用户名,第二个参数也是是否创建永久的Cookie
本例的源代码可以在http://www.aspxfans.com/myBlogFile/窗体身份验证:使用SetAuthCookie方法验证身份.rar 或 http://download.csdn.net/source/1962893下载。
事实上,FormsAuthentication类还有一个GetRedirectUrl()方法,该方法可以返回初始请求页或默认页面,使用该方法也可以跳转页面。如以下代码所示:
[c-sharp] view plain copy
- protected void Button1_Click(object sender, EventArgs e)
- {
- Label1.Text = "";
- //当用户名为1,密码为2时,身份验证通过
- if (TextBox1.Text == "1" && TextBox2.Text == "2")
- {
- //方式三:使用默认的身份验证票,并写入Cookie
- FormsAuthentication.SetAuthCookie("yundao", false);
- //跳转到初始请求页或默认页面
- Response.Redirect(FormsAuthentication.GetRedirectUrl("yundao",false));
- }
- else
- {
- Label1.Text = "用户名为1,密码为2,请重新输入";
- }
- }
GetRedirectUrl()方法的第一个参数为经过身份验证的用户名,但第二个参数是可以忽略的参数,通常设为false。以上代码与FormsAuthentication.RedirectFromLoginPage("yundao", false)是等价的。
本例的源代码可以在http://www.aspxfans.com/myBlogFile/窗体身份验证:使用GetRedirectUrl方法跳转网页.rar 或 http://download.csdn.net/source/1962898下载。
前面介绍了三种窗体身份验证的方法,这三种方法中都使用了默认的身份验证票,而默认的身份验证票中只能传递经过验证的用户名,而不能传递其他数据。如果一个网站中,对不同的身份的用户给予不同的权限,那么以上三种方法都不适用了。由于要传递其他数据,所以必须要使用自定义的身份验证票,如以下代码所示:
[c-sharp] view plain copy
- protected void Button1_Click(object sender, EventArgs e)
- {
- Label1.Text = "";
- //当用户名为1,密码为2时,身份验证通过
- if (TextBox1.Text == "1" && TextBox2.Text == "2")
- {
- //方式四:创建自定义身份验证票
- FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, "yundao", DateTime.Now, DateTime.Now.AddMinutes(30), false, "admin");
- //将身份验证票加密
- string EncrTicket = FormsAuthentication.Encrypt(ticket);
- //创建一个Cookie
- HttpCookie myCookie = new HttpCookie(FormsAuthentication.FormsCookieName, EncrTicket);
- //将Cookie写入客户端
- Response.Cookies.Add(myCookie);
- //跳转到初始请求页或默认页面
- Response.Redirect(FormsAuthentication.GetRedirectUrl("yundao",false));
- }
- else
- {
- Label1.Text = "用户名为1,密码为2,请重新输入";
- }
- }
在以上代码中,首先,使用new FormsAuthenticationTicket()语句创建了一个身份验证票,FormsAuthenticationTicket()的第一个参数为身份验证票的版本号,通常为1;第二个参数为经过验证的用户名;第三个参数为票证发出时的本地时间;第四个参数为票证过期时的本地时间;第五个参数为是否创建永久的Cookie;第六个参数,也就是最重要的参数,为要传递的用户数据。如果不需要传递用户数据,可以设为null。但本人建议使用空字符串,否则为票证加密时可能会产生意想不到的问题。
创建完身份验证票之后,可以使用FormsAuthentication类的Encrypt()方法为身份验证票加密,加密后返回一个字符串。
然后创建一个Cookie,FormsAuthentication.FormsCookieName属性可以返回存放身份验证票的Cookie名,也就是web.config中<Forms>小节的name属性值。当然,在这里还可以设置一些Cookie相关的属性,如Cookie的path、expires、domain等,这此就不多介绍了。
最后,将Cookie写入到客户端。
这样,一个完整的身份验证就完成了,最后还可以选择是否跳转页面。
本例的源代码可以在http://www.aspxfans.com/myBlogFile/窗体身份验证:使用自定义的身份验证票.rar 或 http://download.csdn.net/source/1962904下载。本例的优点是可以传递用户数据,但在传递用户数据时,必须要注册,每个Cookie的大小不能超过4KB,所以传递用户的数据不能太大。
以上四个例子在vs2005中测试通过。
在《asp.net中的窗体身份验证(完整篇之三:用户登录页面) 》中介绍了如何创建一个自定义身份验证票,如何将自定义的身份验证票写入Cookie中。而在自定义的身份验证票中,可以传递一个用户数据,这个用户数据十分有用。可以用来存放用户权限等级、用户VIP号、用户角色等数据,而这些数据往往可以用来控制用户的权限范围。下面我们来看一下怎么样获得用户数据。
首先,修改web.config文件,这个就不再多介绍了,不明白的朋友去查看《asp.net中的窗体身份验证(完整篇之一:创建asp.net的窗体身份验证方式) 》和《asp.net中的窗体身份验证(完整篇之附录:web.config中相应节点详解) 》
其次,创建用户登录页面,在本例中为AdminLogin.aspx,在单击登录按钮后,代码如下所示:
[c-sharp] view plain copy
- protected void Button1_Click(object sender, EventArgs e)
- {
- Label1.Text = "";
- //用户名
- string UserName = TextBox1.Text;
- //密码
- string UserPassword = TextBox2.Text;
- //用户角色
- string UserRole = "";
- //用户身份验证
- if ((UserName == "1" && UserPassword == "2") || (UserName == "3" && UserPassword == "4"))
- {
- //判断用户权限
- if (UserName == "1")
- {
- UserRole = "admin";
- }
- else
- {
- UserRole = "reader";
- }
- //创建一个身份验证票
- FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, "yundao", DateTime.Now, DateTime.Now.AddMinutes(30), false, UserRole);
- //将身份验证票加密
- string EncrTicket = FormsAuthentication.Encrypt(ticket);
- //创建一个Cookie
- HttpCookie myCookie = new HttpCookie(FormsAuthentication.FormsCookieName, EncrTicket);
- //将Cookie写入客户端
- Response.Cookies.Add(myCookie);
- //跳转到初始请求页或默认页面
- Response.Redirect(FormsAuthentication.GetRedirectUrl("yundao",false));
- }
- else
- {
- Label1.Text = "用户名为1,密码为2,或用户名为3,密码为4。请重新输入。";
- }
- }
在本例中,为了简单起见,只要用户名为1、密码为2,或用户名为3、密码为4,都认为身份验证通过。身份验证通过后,创建一个身份验证票,如果用户名为1,身份验证票中传递的用户数据为“admin”,如果用户名为3,身份验证中传递的用户数据为“reader”。然后加密身份验证票,写入客户端Cookie。
第三,创建一个test.aspx文件,在该文件中获得用户数据,如以下代码所示。
[c-sharp] view plain copy
- protected void Page_Load(object sender, EventArgs e)
- {
- //获得存放身份验证票的Cookie值,这个值是经过加密的
- string EncrTicket = Request.Cookies[FormsAuthentication.FormsCookieName].Value;
- //获得身份验证票
- FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(EncrTicket);
- //从身份验证票中提取经过验证的用户名
- string UserName = ticket.Name;
- //从身份验证票中提取用户数据
- string UserData = ticket.UserData;
- Label1.Text = UserName + ",您好!您的权限为:" + UserData;
- }
在以上代码中,先使用Request.Cookies获得存放身份验证票的Cookie,其中Cookie名可以通过FormsAuthentication类的FormsCookieName属性获得。
由于身份验证票是加密后存放在Cookie中的,所以在获得Cookie值之后,必须将身份验证票解密。FormsAuthentication类的Decrypt()方法可以将加密后的身份验证票(也就是存放在Cookie中的字符串)解密,并返回解密后的身份验证票。在asp.net中使用FormsAuthenticationTicket类来代表身份验证票。
获得解密后的身份验证票后,可以通过FormsAuthenticationTicket类的Name属性返回经过验证的用户名,也可以通过UserData属性获得用户数据。
前面介绍过了如何使用Forms方式进行用户身份验证,然而,在大多网站中都会有一个“退出”功能,让用户可以通出登录。在asp.net中,退出的方式很简单,只要在退出页面中加上代码“FormsAuthentication.SignOut()”即可。
你可以使用Response.Redirect()在退出之后将网页跳转到另一个网页,也可以使用“FormsAuthentication.RedirectToLoginPage()”将网页跳转到登录页面。
在《asp.net中的窗体身份验证(最简单篇)》说明了如何让通过了身份验证的用户访问网站,但是该方法中有一个缺点,就是访问整个网站都需要用户身份验证。因此,在《asp.net中的窗体身份验证(分目录验证篇)》中介绍了如何让通过了身份验证的用户访问特定的目录,例如网站根目录中的网页是任何用户都能访问的,而admin目录则只能让通过身份验证的用户访问。这种方法可以解决大部分网站的身份控制,但是,对于一 些复杂的网站而言, 这种方式就有点捉袊见肘了。例如,有一个网站,我们要求根目录中的网页可以让任何用户浏览,而admin目录中的网页只有管理员才能访问、backup目录中的网页只有管理员和系统备份员才能访问、user目录中的网页除了匿名用户之外的所有用户都能访问,对于这种情况,那要怎么办呢?是不是还可以使用Forms身份验证呢?
答案是肯定的,使用Forms身份验证可以通过角色的方式来达到上述目的。在本例中,我们可以先设定三种角色:admin、backup和reader,其中,admin角色也就是管理员角色,该角色可以访问admin目录、backup目录和user目录;backup角色也就是系统备份员角色,该角色不能访问admin目录,但可以访问backup目录和user目录;而reader角色是普通用户角色,该 角色不能访问admin目录、backup目录,只能访问user目录。
第一步、设置Forms身份验证方式和登录网页。
要达到上述目的,首先我们需要修改web.config文件,这次修改的目的是让网站拥有Forms身份验证功能(详见《asp.net中的窗体身份验证(最简单篇)》、《asp.net中的窗体身份验证(分目录验证篇)》和《asp.net中的窗体身份验证(完整篇之附录:web.config中相应节点详解)》),修改方式是:在<system.web>和</system.web>中找到<authentication>节,将其改为“<authentication mode="Forms"></authentication>”,其中Forms代表使用表单认证。
当然,你也可以在web.config文件中设置用户登录的网页,设置方法为:在<authentication mode="Forms">和</authentication>节点中添加“<forms loginUrl="~/login.aspx"></forms>”代码,其中loginUrl参数用于说明录页面,如果没有设置该参数的话,默认的登录网页就是网站根目录的login.aspx网页。完整的代码如下所示:
[c-sharp] view plain copy
- <authentication mode="Forms"><!--验证方式为窗体验证-->
- <forms loginUrl="~/login.aspx"></forms><!--登录网页为login.aspx-->
- </authentication>
第二步、设置默认的用户访问方式。
在本例中,我们要求是网站根目录中的网页可以让匿名用户访问,因此,在设置完Forms身份验证方式之后,我们还要设置默认的用户访问方式——也就是让所有的匿名用户访问。设置方式是:在<system.web>和</system.web>节点中添加以下代码:
[c-sharp] view plain copy
- <authorization>
- <allow users="?"/><!--默认为所有用户都能访问-->
- </authorization>
第三步、设置不同目录的访问方式。
在《asp.net中的窗体身份验证(分目录验证篇)》中,曾经介绍过分目录的验证方式,这种方式是在目录中创建一个web.config文件,然后在web.config文件中的<system.web>和</system.web>小节中,添加“<authorization></authorization>”节点,在该节点中设置不让匿名用户访问。在这里,我们介绍另外一种设置不同目录的访问方式,假设,我们现在要指定admin目录不能让匿名用户访问,那么,我们可以在根目录的web.config文件的<system.web>和</system.web>中添加以下代码:
[c-sharp] view plain copy
- <location path="admin">
- <system.web>
- <authorization>
- <!--拒绝所有匿名用户访问-->
- <deny users="?"/>
- </authorization>
- </system.web>
- </location>
在以上代码中,<location>节点的path参数用于说明要对哪个目录可以访问控制,而<authorization>节点的作用同样是用于设置具体的访问权限。在以上代码中,<deny>节点用于说明拒绝哪些用户访问,如果需要拒绝所有用户访问,那么就要使用到“<deny users="*"/>”代码;如果需要允许所有用户访问,那么就需要使用“<allow users="*"/>”代码。然而,在<deny>节点和<allow>节点中还可以设置拒绝或允许哪些角色的用户访问,设置方法为“<deny roles="admin"/>”或“<allow roles="admin"/>”。其中,roles参数用于说明指定的拒绝或允许访问的角色名,如果存在多个角色,则用逗号进行分割。在本例中,要求admin目录只能让admin角色的用户访问、backup目录可以让admin角色和backup角色的用户访问,user目录可以让所有能过身份验证的用户访问(无论是什么角色),可以使用以下代码进行设置:
[c-sharp] view plain copy
- <!--设置admin目录的访问权限-->
- <location path="admin">
- <system.web>
- <authorization>
- <!--允许角色为admin的用户可以访问-->
- <allow roles="admin"/>
- <!--拒绝所有其他用户访问-->
- <deny users="*"/>
- <!--注意以上两句的次序不能变-->
- </authorization>
- </system.web>
- </location>
- <!--设置backup目录的访问权限-->
- <location path="backup">
- <system.web>
- <authorization>
- <!--允许角色为admin和backup的用户可以访问-->
- <allow roles="admin,backup"/>
- <!--拒绝所有其他用户访问-->
- <deny users="*"/>
- <!--注意以上两句的次序不能变-->
- </authorization>
- </system.web>
- </location>
- <!--设置user目录的访问权限-->
- <location path="user">
- <system.web>
- <authorization>
- <!--拒绝所有匿名用户访问-->
- <deny users="?"/>
- </authorization>
- </system.web>
- </location>
第四步、添加登录页面。
由于在本例中,指定了网站目录中的login.aspx网页为登录面页,因此,在网站的根目录中添加一个login.aspx文件。然后在该文件中添加一个文本框(用于输入用户名)和一个密码框,再添加一个确定按钮。为了方便起见,我们假设用户名和密码都为1时,登录用户的角色为admin;用户名和密码都为2时,登录用户的角色为backup;用户名和密码都为3时,登录用户的角色为reader。登录网页中的代码如下所示:
[c-sharp] view plain copy
- //用户名
- string UserName = TextBox1.Text;
- //密码
- string UserPassword = TextBox2.Text;
- //用户角色
- string UserRole = "";
- //用户身份验证
- if ((UserName == "1" && UserPassword == "1") || (UserName == "2" && UserPassword == "2") || (UserName == "3" && UserPassword == "3"))
- {
- //判断用户权限
- switch (UserName)
- {
- case "1":
- UserRole = "admin";
- break;
- case "2":
- UserRole = "backup";
- break;
- case "3":
- UserRole = "reader";
- break;
- }
- //创建一个身份验证票
- FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, "yundao", DateTime.Now, DateTime.Now.AddMinutes(30), false, UserRole);
- //将身份验证票加密
- string EncrTicket = FormsAuthentication.Encrypt(ticket);
- //创建一个Cookie
- HttpCookie myCookie = new HttpCookie(FormsAuthentication.FormsCookieName, EncrTicket);
- //将Cookie写入客户端
- Response.Cookies.Add(myCookie);
- //跳转到初始请求页或默认页面
- Response.Redirect(FormsAuthentication.GetRedirectUrl("yundao", false));
- }
以上代码和前面介绍过的身份验证代码类似,惟一不同的是将用户角色写在了身体验证票的用户数据(也就是FormsAuthenticationTicket()方法的最后一个参数)中。
第五步、将用户角色添加到表示用户身份的GenericPrincipal对象中。
经过上面的步骤,我们已经将用户的角色存放在身份验证票据中了,然而,此时用于表示用户身份的GenericPrincipal对象中却还只是包含了用户信息而不包含用户的角色信息。在这一步骤中,我们必须将用户角色信息存放到GenericPrincipal对象中。至于怎么样将用户角色信息存放到GenericPrincipal对象中呢,我们就必须要先了解一下用户验证与授权的过程了:
1、客户端浏览器发送一个请求到服务器端。
2、服务器端接收到请求之后,会先检查该请求中是否包含有可能存在的恶意标记,如果存在恶意标记则会返回错误信息。
3、激活作为HTTP执行管线链中的第一个事件——BeginRequest事件。
4、处理完BeginRequest事件之后,asp.net将会创建用户标识,此时激发AuthenticateRequest事件,该事件可以确保事件处理程序之前对请求进行身份验证。
5、在AuthenticateRequest 事件之后,会继续触发PostAuthenticateRequest 事件,该事件同时是在asp.net创建用户标识时触发,但在该事件中可以访问由AuthenticateRequest事件中处理的任何数据。
6、然后,asp.net会进入用户授权验证,此时会激发AuthorizeRequest事件。
7、验证授权完毕之后,当用户获得了授权时,将会激发PostAuthorizeRequest事件。
8、到第7步为止,整个用户身份验证就已经完毕了,接下来将会激发PostResolveRequestCache事件,在该事件之后还会有N个事件,直到向客户端返回信息为止。但那件事件都不是本文的重点,也就不详细介绍了。
在用户登录确认身份之后,通常都会将身份验证票写到客户端的cookie中,然后客户端会将信息发送到服务器上,而在服务器端,asp.net会为每一个HTTP请求都分配一个HttpApplication对象,该对象的作用是处理HTTP请求的,也就是用来处理以上步骤的。从以上步骤中可以看出,身份验证是从AuthenticateRequest事件激发开始的,在该事件触发后,asp.net就会根据客户端发来的包含身份验证票的Cookie建立用户身份。
而asp.net创建的用户身份包含在HttpContext.User属性中,可以使用“HttpContext.Current.User”来获得得这个用户身份。对于Forms验证而言,HttpContext.User属性是一个GenericPrincipal对象,该对象有两个重要的属性:第一个是Identity属性,该属性存放的是当前用户的标识。Identity属性就是一个FormsIdentity对象,该对象的Name属性可以用户标识、Ticket属性可以获得用户的身份验证票。另外,GenericPrincipal对象还有一个m_role属性,这个属性是GenericPrincipal对象的私有属性,所以不能通过GenericPrincipal.m_role来访问,该属性是一个数组,用于存放用户角色。如果想知道用户是否属于某个角色,可以通过GenericPrincipal 对象的IsInRole()方法来判断。然而,由于身份验证票中并没有提供m_role参数,因此,在asp.net创建用户身份时,用户是没有角色信息的。换句话而言,对于Forms身份验证而言,在默认情况下,GenericPrincipal对象的m_role属性为空。
因此,如果想要使用角色的用户身份验证方式,就必须要在代表用户身份GenericPrincipal对象中添加用户角色。而且,这个工作必须要在第3个步骤中完成,因为第4个步骤的作用就是创建用户标识。因此,我们必须要在网站的根目录中创建一个Global.asax文件,Global.asax文件的作用就不用我再多说了了吧?
在Global.asax文件中添加代码“protected void Application_AuthenticateRequest(object sender, EventArgs e)”用于处理AuthenticateRequest事件。处理步骤如下所示:
1、首先判断用户的身份验证信息是否为空,如果HTTP请求中就没有用户身份验证信息,那么后面的步骤也就没必须进行了。前面介绍过,asp.net创建的用户身份包含在HttpContext.User属性中,因此,在该步骤中只需要判断HttpContext.User属性是否为空即可达到目的。
2、然后判断用户是否已经通过了身份验证,如果没有通过身份验证,那么在访问受限网页时,asp.net会自动跳转到登录网页上。前面介绍过,HttpContext.User属性也就是一个GenericPrincipal对象,该对象的Identity属性值为用户标识,那么只要判断Identity.IsAuthenticated是否为true,就可以知道用户是否已经通过身份验证。
3、由于asp.net的身份验证方式有多种,因此,在这里我们还要判断一下用户通过的身份验证方式是否为Forms验证方式。要达到该目的,只需要判断User.Identity是否为FormsIdentity对象即可。
4、然后,可以获得已经通过了Forms身份验证的用户标识,再从该用户标识的身份验证票中获得用户数据(也就是包含了用户角色的字符串)。
5、再然后,将用户角色字符串放到一个角色数组中——如果一个用户属于多个角色,通常我们都会将不同的角色使用逗号进行分割后放在用户数据中,然后将第4步骤中获得的用户数据分割后放在一个数组中,这样就得到了一个角色数组。
6、最后,创建一个新的GenericPrincipal对象,在创建该对象时将从第4步骤中获得的用户标识以及第5步骤中获得的角色数组一起放入GenericPrincipal对象,然后将该对象赋值给HttpContext.User。
这样,用户身份中就包含了角色信息。在随后的访问用,asp.net就会自动验证用户的角色以判断该用户是否可以访问限制的目录了。具体代码如下所示:
[c-sharp] view plain copy
- protected void Application_AuthenticateRequest(object sender, EventArgs e)
- {
- //判断正在请求页的用户的身份验证信息是否为空
- if (HttpContext.Current.User != null)
- {
- //判断用户是否已经进行了身份验证
- if (HttpContext.Current.User.Identity.IsAuthenticated)
- {
- //判断当前用户身份验证的方式是否为Forms身份验证方式
- if (HttpContext.Current.User.Identity is FormsIdentity)
- {
- //获得进行了Forms身份验证的用户标识
- FormsIdentity UserIdent = (FormsIdentity)(HttpContext.Current.User.Identity);
- //从身份验证票中获得用户数据
- string UserData = UserIdent.Ticket.UserData;
- //分割用户数据得到用户角色数组
- string[] rolues = UserData.Split(',');
- //从用户标识和角色组初始化GenericPrincipal类
- HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(UserIdent, rolues);
- }
- }
- }
- }
至此,一个完整的带角色控制的窗体身份验证就已经做完了。
在前面的几篇文章中介绍了asp.net的窗体身份验证,这种身份验证方式可以让通过验证的用户访问指定的目录,而没有通过验证的用户不能访问该目录下的网页。
但是,有一种例外,就是目录中的html文件例外。例如,在《asp.net中的窗体身份验证(最简单篇)》中介绍的,除了登录网页之外,所有网页都必须在登录之后才能访问,但如果在网站中添加一个HTMLPage.htm文件,访问该文件是不需要身份验证的。如下图所示:
ASP.NET Forms验证详解 ASP.NET Forms验证详解 之所以出现这种情况,是由IIS的访问机制决定的。当客户端浏览器向Web服务器发送请求时,Web服务器会对所请求的文件的扩展名进行检查,然后决定由哪个ISAPI扩展处理这个请求,然后将请求传递给该ISAPI扩展。而asp.net事实上也就是一个ISAPI扩展,对于asp1.1而言,这个扩展文件为:C:/WINDOWS/Microsoft.NET/Framework/v1.1.4322/aspnet_isapi.dll,对于asp2.0而言,这个扩展文件为:c:/windows/microsoft.net/framework/v2.0.50727/aspnet_isapi.dll。
很不幸的是:asp.net映身的扩展名有.aspx、.ascx、.ashx、.asmx,就是没有.htm和.html。所以,如果受限目录中包括.htm和.html网页时,asp.net是不会对其进行处理的。只要用户可以通过IIS的权限控制,就可以访问这些文件。
找到原因就好办了,如果想要让.htm文件和.html文件也受asp.net控制,那么,就必须添加映射。添加方法如下所示:
1、在IIS中找到网站或虚拟目录,右击,选择属性,打开如下图所示窗口。
2、单击“配置”按钮,弹出下图窗口。ASP.NET Forms验证详解 3、单击“添加”按钮,打开如下窗口。ASP.NET Forms验证详解 ASP.NET Forms验证详解 4、在可执行文件中输入“C:/WINDOWS/Microsoft.NET/Framework/v1.1.4322/aspnet_isapi.dll”(asp.net1.1)或“c:/windows/microsoft.net/framework/v2.0.50727/aspnet_isapi.dll”(asp.net 2.0),在扩展名中输入“htm”或“html”,注意前面不用加“.”。然后点确定。
5、重启一下IIS,再访问刚才的html文件,如下图所示,现在要求登录了,而且在URL中也可以看到ReturnUrl参数值为htm文件。
ASP.NET Forms验证详解 原本是没打算写下这一篇的,因为觉得身份验证到了登出之后,就算完成了。可是后来有博友提出疑问,登出之后,点击浏览器上的后退按钮,还是可以退回到登出前页面,起不到身份验证的作用。
事实上,就算使用后退按钮回到原来的页面,也只能看看页面内容,而不能对网页进行操作。一但进行操作的话,还是会需要重新登录的。
也许,有些朋友对页面的安全性要求比较高,连看都不想让人家看到。那这要怎么办?
这要从浏览器的缓存说起了。在N久以前,网络的宽带慢的是很慢很慢,所以,浏览器都有一个叫做“缓存”的功能。当你浏览了网页之后,就会把这个网页先缓存到本地计算机中,等你下一次再访问该网页时,浏览器会将缓存到本机计算机中的内容与服务器上的内容进行比较,如果没有更新,就不从服务器上读取网页,而是直接显示本地计算机中缓存的内容。这样做有两个好处,第一、客户端可以快速打开网页,节省等待的时间;第二、减少服务器压力。
所以,这个功能就一直保留了下来,直到今天、明天或者后天。
如果想不让浏览器缓存网页,最简单的方法就是使用HTML代码来实现,如以下代码所示。
[xhtml] view plain copy
- <meta http-equiv="Cache-Control" content="no-cache" />
- <meta http-equiv="Pragma" content="no-cache" />
- <meta http-equiv="Expires" content="-1" />
以上代码应该放在<head>和</head>之间,第一句是HTTP 1.1标准中支持的,让浏览器不缓存网页内容;第二句是HTTP 1.0标准中支持的,让浏览器不缓存网页内容。因为不知道用户浏览器支持什么标准,所以可以把两句都加上。第三句,指定是网页过期时间,一般来说content值都是指定的一个时间,在这个时间之前,浏览器会显示缓存中的内容,这个时间之后,才会从服务器中读取新内容。如果为0或负数,那么就说明浏览器永远都从服务器中读取网页内容。
根据我十年前写HTML的经验,这样写就完全没有问题了。可是没想到的是……测试结果让我大跌眼镜、IE8、chrome6、Opera10和FireFox3.5下的测试全部没通过,点击浏览器的后退,还是可以看到登出后的网页。
还好,我还有九年前编写ASP的经验,于是,在.CS文件中加入了以下代码:
[c-sharp] view plain copy
- //禁止客户端缓存服务器上的网页
- Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);
- //添加HTTP头
- //HTTP 1.0标准,作用相当于<meta http-equiv="Pragma" content="no-cache" />
- Response.AddHeader("pragma", "no-cache");
- //HTTP 1.1标准,作用相当于<meta http-equiv="Cache-Control" content="no-cache" />
- Response.AddHeader("Cache-Control", "no-cache");
- //设置不缓存
- Response.CacheControl = "no-cache";
- //设置在浏览器上缓存的页面的过期时间
- Response.Expires = -1;
- //从缓存中移除的日期
- Response.ExpiresAbsolute = DateTime.Now.AddSeconds(-1);
曾经,在ASP中使用了类似于以上代码的代码,那真是天下无敌,无论是IE浏览器还是Netscape浏览器(多让人怀念的浏览器啊),都能完美地实现不能后退的功能。
美滋滋地进行了测试,这回,又让我跌了一次眼镜居然除了IE8(我没安装别的版本的IE)能实现不能后退的功能之外,chrome6、Opera10和FireFox3.5下的测试全部没通过。
最后,我不得不使用asp.net的绝招了,在CS中加上以下代码:
[c-sharp] view plain copy
- Response.Cache.SetNoStore();
这回的测试结果还算满意,IE8、chrome6和FireFox3.5都通过测试,只有Opera10还是很顽强地从缓冲中读取网页内容。至于怎么让Opera浏览器也实现网页过期,目前我还没有找到方法。如果有哪位朋友知道的话,请告诉我,谢谢。
在做测试时,以上代码写在子目录的测试页里,测试方法为:先进入登录页面-->登录后进入测试页面-->进入登出页面-->单击浏览器的后退按钮回退到测试页面。
如果想在登出之后,使用后退按钮不能回退到任何一页,那么就可以在任何一个网页中加入以上代码。当然,还有一个简单的方法,在Global.asax文件中加入以下代码:
[c-sharp] view plain copy
- protected void Application_BeginRequest(Object sender, EventArgs e)
- {
- HttpContext.Current.Response.Cache.SetNoStore();
- }
http://m.blog.csdn.net/blog/smallfools_11109/4221118