Web项目最终是要在企业内部网( Intranet )或者互联网( Internet )环境下部署的. 即便是在企业Intranet环境下, 也存在跨国企业或者连锁式分布企业, 于是将会有这样的需求: 通过互联网将同一企业的多个局域网互联起来. 因此, 无论是Intranet还是Internet都具有严格的安全要求, 可以设想一下: 允许任意用户访问其所有资源的web项目将严重降低用户信任度, 对于电子商务类的网站更是无法接受. 即使是”资料共享”类的网站项目(如: 个人博客、图书馆等), 其后台管理界面也不会允许任意用户访问的.
可见, 无论是何种类型的Web项目, 安全都是无法回避的课题. 那么, 安全是如何保证的呢? “安全”是个包括用户、机房环境、服务器、防火墙、信息服务提供商(ISP)、网站项目编码、数据库等等一系列相关问题在内的系统工程, 其范畴涉及从用户发送请求开始到接收响应的通信过程中所有相关事务. 听起来好像很复杂, 实际上也确实是, 但人类的聪明之处在于: 总能够将非常复杂的东西分解为一个个相对简单的部分. 在保证Web项目安全的过程中, 涉及到很多方面, 每一个方面总能够找到相关职位的人来处理, 如: 服务器安全有网络工程师、数据库安全有DBA、通信过程的安全有ISP等等. 对于Web项目的开发人员来说, 安全就是: 保证源代码不被非法用户获取或修改、不同类别的用户只能访问其允许那部分资源.
对于Web开发人员的第一种安全: 主要通过加密手段来处理. 如: 在代码中对用户名和密码等敏感信息进行加密处理、对关键的配置文件(*.config)进行加密处理、对关键类库文件(*.dll)进行强名称处理并放到GAC中.
对于Web开发人员更为重要的第二种安全, 需要解决两个方面的问题: 第一、用户身份验证, 即判断用户是否为已登录的合法用户. 第二、授予用户相应的权限, 对已验证身份的用户, 授予或拒绝特定的权限来限制用户访问. 在.NET技术中, ASP.NET组件与Framework及IIS协同工作以提供Web应用程序的安全要求. 参见下图, 表示了Asp.Net身份验证及授权管理信息.
Http协议是断开的访问协议, 也就是说在客户端发送请求并得到服务器的响应后, 服务器将销毁页面对象, 当客户端再次请求时将重新创建一个新的页面对象. 所以, 验证用户的工作需要在每一次请求中都进行处理. Asp.Net采用的解决方案是在客户端浏览器(Cookie)中保存一个表示用户的票据, 在浏览器的每次请求中都附带这个票据, 服务器收到请求后检查这个票据. 在客户端注销的时候, 就删除客户端保存的票据.
1. 在Asp.Net中支持四中验证模式: Windows验证、Forms验证、Passport验证、None.
这四种验证方式中, Passport验证是微软的专有技术, 它允许用户使用Passport帐户同时管理其操作系统、邮件、MSN、LiveSpace等多种应用. 也就是只要用户登录了Passport帐户, 就可以直接访问操作系统、邮件、MSN等程序而不再需要进行身份验证操作. 但是由于版权、使用费用的问题, 似乎没有流行开来. None表示不验证, 没有什么可说的. 最终, 在4种验证方式中, 真正使用的只有Windows验证和Forms验证两种, 也是本篇主要讨论的问题.
由上图可以看出, 无论是Windows验证还是Forms验证, 其关于验证过程的处理流程是很相似的, 区别是验证和颁发票据的时机不同: Windows验证是在IIS和服务器的帐户管理功能之间验证客户端用户并发送票据(或者称为授权信息Authorization); 而Forms验证是在HttpApplication处理管道的页面处理流程中完成的, 具体是在页面处理流程的PageLoad和PageLoadComplete事件之间执行的控件的Click事件中完成验证客户端的工作并发送票据的.
2. 先来看Windows验证:
配置Windows验证: 在IIS中网站项目上右击并选择”属性”, 在”目录安全性” ---> ”认证和访问控制”中点击”编辑”后, 去掉”匿名访问”, 只勾选”集成Windows验证即可”. 之后再网站的配置文件(web.config)中, 找到system.web配置节中找到authentication元素的mode属性, 将其值修改为Windows后, 就完成了Windows验证方式的配置.
<system.web>
<authentication mode = ”Windows” />
</system.web>
Windows验证的请求过程:
当客户端浏览器(通常是IE)向Web服务器发送第一个请求(Get请求)时, 服务器中IIS接收到请求后先检查自身配置, 发现需要进行Windows身份验证而客户端请求中没有附带任何身份验证信息, 因此服务器直接返回401未授权信息给客户端并提示客户端需要输入用户名和密码. 当客户端提供了账户名和密码后, 第二次发送Get请求到服务器的IIS(该请求中附带了Authorization授权信息), IIS接受到请求后将到服务器的用户管理组件中, 验证是否有该用户. 如果服务器上没有该用户, 则IIS会仍然返回401未授权信息给客户端; 如果服务器上有该用户, 那么Windows操作系统将会获取当前用户的信息并传送给IIS, 之后IIS再将用户信息通过aspnet_iisapi.dll传递给Asp.Net网站应用程序. 之后就进入了HttpApplication处理管道, 在HttpApplication的验证事件(AuthenticateRequest)中, 网站应用程序将利用IIS传递的用户信息构建System.Security.Principal.WindowsPrincipal类型的对象用于在程序中表示当前请求的用户对象, 这个对象同时包含用户和用户组信息, 我们可以通过HttpContext对象中的User属性在管道的处理流程中获得用户信息, 也可以在页面对象(Page)的User属性中, 获得用户信息.
Window身份验证方式要求客户端的浏览器必须使用IE浏览器, 只有IE浏览器可以与IIS服务器传递Windows帐户信息, 这种方式非常适合在企业内部管理系统中使用, 但是不适合在互联网环境中使用.
示例代码:
//WindowsAuthenticate.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="WindowsAuthentication.aspx.cs" Inherits="WindowsAuthentication" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:TextBox ID="WinAuthen" runat="server"></asp:TextBox>
</div>
</form>
</body>
</html>
//WindowsAuthenticate.aspx.cs
//WindowsAuthenticate.aspx.cs
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
public partial class WindowsAuthentication : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
this.WinAuthen.Text = User.Identity.Name + ":" + User.Identity.AuthenticationType;
}
}
3. 再来看Forms验证:
在介绍Forms验证之前, 先说说应用程序中的用户表示:
由于各种各样的应用程序中都可能需要对用户进行管理, .NET专门定义了在程序中表示用户的方式: System.Security.Principal命名空间下的IPrincipal定义了用户对象的抽象接口, IIdentity定义了用户标识的抽象接口, 其中IPrincipal中定义了一个返回值为IIdentity的属性Identity和方法IsInRole(stirng role); IIdentity中定义了用户验证类型(AuthenticateType)、是否通过验证(IsAuthenticated)、当前用户名(Name)三个属性.
.Net同时还为我们定义了多种上述接口的实现类, 常见的有普通用户验证(GenericIdentity、GenericPrincipal)、Windows用户验证(WindowsIdentity、WindowsPrincipal)、Forms验证(System.Web.Security.FormsIdentity, 需要配合System.Security.Principal中的GenericPrincipal使用)
关于Forms验证:
配置Forms验证: 在IIS中网站项目上右击并选择”属性”, 在”目录安全性” ---> ”认证和访问控制”中点击”编辑”后, 只勾选”匿名访问”即可(千万不要修改默认的用户和密码). 之后再网站的配置文件(web.config)中, 找到system.web配置节中找到authentication元素的mode属性, 将其值修改为Forms, 并在authentication配置节中, 添加类似于
<form name=”Cait” loginUrl=”login.aspx” defaultUrl=”default.aspx” protection=”All” ></forms>的配置信息, 关于该配置中的几个属性:
loginUrl、defaultUrl分别表示登陆页和默认首页的url
name是保存Ticket的Cookie的名称
protection是对Cookie的保护措施(Encryption表示仅加密、Validation表示仅签名检查、All表示前两项都进行、None表示不保护)
slidingExpiration用来设置Cookie的过期方式, true表示使用滑动过期时间, false表示绝对过期时间(默认false)
timeout用类设置Cookie的过期时间, 以”分钟”为单位. 若slidingExpiration为滑动过期, 则timeout表示滑动过期的时间间隔; 若slidingExpiration是绝对过期, 则timeout表示绝对过期的时间为登陆时间+timeout的值.
Forms验证的请求过程:
当客户端浏览器第一次向服务器发GET请求时, 服务器中的IIS接收请求. IIS检查请求资源的后缀名, 如果是*.htm等静态资源, IIS会直接找到资源并返回给客户端浏览器, 如果不是*.htm等静态资源, 则IIS会先查找”应用程序配置”中的”扩展名配置列表”; 如果找不到相关扩展名, 则IIS将返回无法找到资源的错误, 如果找到了, 则将请求转发给处理该类请求的动态类库处理(.net技术中通常为aspnet_iisapi.dll).
aspnet_iisapi.dll首先查找配置文件, 根据配置文件找到\相关资源的处理程序. 之后由w3wp.exe进程创建应用程序域, 并在应用程序域中创建HttpRuntime执行环境, 之后再创建HttpContext对象(其中含HttpRequest、HttpResponse、Cookie、Session等对象)后进入应用程序处理管道, 开始请求处理.
在应用程序处理管道的十几个事件中, 在第二个事件(AuthenticateRequest事件)中开始身份验证过程, 如果身份验证失败(无票据Ticket), 则用户名为空串, 相应的页面对象的User对象的Identity中的name也是空串, 如果验证成功(有Ticket)则通过Cookie获得票据信息, 进程创建程序中的用户表示(例如: 可以通过页面的User对象获得用户信息), 然后继续执行应用程序处理管道事件. 在执行完PreRequestHandlerExecute事件之后, 将进入页面处理管道(页面处理流程). 在页面处理流程中, 如果用户名为空串, 则IsAuthentication方法返回false, 这是通常根据配置文件中的<forms>配置节, 直接重定向到Login页面. 如果用户名不为空, 表示认证通过, 则绘制客户端所请求的页面并继续走完应用程序处理管道后, 将请求页面发送给客户端浏览器.
由于客户输入域名, 直接请求的是默认页default.aspx, 由于第一次请求没有附带票据, 所以用户名肯定为空, 则根据配置节中的<forms>直接重定向到Login页面.
在第二次请求中, 客户端浏览器向服务器发送Post请求, 服务器IIS接受请求后, 仍然按照aspnet_iisapi.dll ---> w3wp.exe ---> HttpRuntime ---> HttpApplication 的顺序处理. 由于请求的是Login.aspx页面, 默认情况下, 这个页面任何人都具有访问权限, 因此IsAuthenticate()方法可以通过. 在之后的页面处理流程中, 将激发登录按钮的事件处理器, 我们在按钮的事件处理器中执行创建票据和发票的过程, 并将票据保存在客户端浏览器中. 然后, 继续走完应用程序处理流程.
在之后的第三次请求中, 客户端浏览器首先检查本地的Cookie, 如果发现Cookie文件的名称和请求资源的域名相同, 则客户端浏览器会讲Cookie中的内容以键值对的方式一并发送到服务器中. 服务器端IIS仍然走应用程序处理管道, 在其第二个事件(AuthenticateRequest事件中), 发现Cookie不为空时将从Cookie去中的帐户信息, 保存在HttpContext的User对象中, 于此同时页面对象中也有一个User的属性, 该属性其实就是HttpContext的User, 我们可以再页面后台文件(.cs)中, 通过页面对象的User获得帐户信息. 在之后的第四次、第五次等数次请求中, 验证过程都在每次请求的AuthencateRequest事件中进行了处理, 我们可以很方便的从页面对象中取得用户名称、用户组等信息. 如果用户没有点击注销, 那么该用户的信息将始终存在, 在用户点击了注销按钮后, 讲调用SingOut()方法, 将Cookie中的票据删除(修改Cookie过期时间), 之后用户再次请求将直接重定向到Login页面. 我们可以通过子在配置文件的<authorization>配置节(不常用)或<location>配置节中规定哪些用户可以访问, 这样就可以实现比较完善的管理功能了.
a). 使用系统的System.Web.Security.FormsAuthentication类:
Asp.Net中默认已经实现了对于Forms验证的直接支持, 因此我们可以直接使用System.Web.Security.FormsAuthentication实现类来完成Forms验证操作.
值得注意的是: 使用系统默认的Forms验证方式, 一定要自定义登陆窗体(页面). 因为默认的登录窗体名称为Login.aspx, 此名称可以在配置文件的forms元素中进行配置, 但是Asp.net网站会根据Login.aspx创建Login的类型, 而.Net类库System.Web.UI.WebControls中也有一个名称为Login的控件类型, 这样便会引起冲突, 因此一定要为Login.aspx登陆页面自定义一个命名空间以避免冲突.
FormsAuthentication类的RedirectFromLoginPage()方法实现了登陆处理中的创建票据、创建Cookie、加入到Response中的工作, 然后将用户送回原来访问的Url, 若直接访问Login页面则将用户送回网站首页. 注意: 该方法没有过期时间的参数, 过期时间的设置在配置文件web.config中的forms元素的slidingExpiration和timeout属性中配置.
也可以使用SetAuthCookie()方法仅创建票据, 而由程序员决定登陆之后的重定向.
注销就是删除用户的Cookie, 可以采用SingOut()方法进行, 不过在执行SingOut()方法后需要调用RedirectToLoginPage()方法将用户重定向到登陆页面.
//为简单起见, 这里直接将用户名和密码写在<forms>元素中的<credentials>节点中.
//LoginPage.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="LoginPage.aspx.cs" Inherits="LoginPage" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:TextBox ID="txt_username" runat="server"></asp:TextBox><br />
<asp:TextBox ID="txt_userpass" runat="server"></asp:TextBox><br />
<asp:LinkButton ID="lbtn_login" runat="server" onclick="lbtn_login_Click">登陆</asp:LinkButton>
</div>
</form>
</body>
</html>
//LoginPage.aspx.cs
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
public partial class LoginPage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void lbtn_login_Click(object sender, EventArgs e)
{
if (IsValid)
{
string name = this.txt_username.Text.Trim();
string pass = this.txt_userpass.Text.Trim();
//注意: 未知的编码方面的原因, 造成web.config的<credentials>节点用md5计算散列码, 结果不正确.
//通常做法: 避免向服务器端传递明文, 可以先在Javascript中计算MD5散列码, 然后再发送到服务器端. 服务器端接收到后需要掉DAL并到数据库中验证.
//获得用户输入密码的散列码:
byte[] buffer;
buffer = System.Text.Encoding.GetEncoding("utf-8").GetBytes(pass);
buffer = System.Security.Cryptography.MD5CryptoServiceProvider.Create().ComputeHash(buffer);
string password = BitConverter.ToString(buffer).Replace("-", "");
//string password = FormsAuthentication.HashPasswordForStoringInConfigFile(pass,"SHA1");
if (FormsAuthentication.Authenticate(name, password)) //是否通过验证
{
FormsAuthentication.RedirectFromLoginPage(name, false);
//FormsAuthentication.SetAuthCookie(name, false); //仅创建票据, 手动跳转
//this.Response.Redirect("default.aspx");
}
}
}
}
//Default.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lbl_msg" runat="server" Text="Label"></asp:Label>
<asp:LinkButton ID="logout" runat="server">注销</asp:LinkButton>
</div>
</form>
</body>
</html>
//Default.aspx.cs
using System;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
this.lbl_msg.Text = this.User.Identity.IsAuthenticated ? this.User.Identity.Name + " 已登录! " : " 您还未登录! ";
}
protected void logout_Click(object sender, EventArgs e)
{
System.Web.Security.FormsAuthentication.SignOut();
}
}
//web.config
<authentication mode="Forms">
<forms name="Cait" loginUrl="LoginPage.aspx" defaultUrl="Default.aspx" protection="All">
<credentials passwordFormat="Clear">
<user name="Admin" password="0192023A7BBD73250516F069DF18B500" />
</credentials>
</forms>
</authentication>
<authorization>
<deny users="?"/>
</authorization>
b). 使用Global.aspx或IHttpModule实现类来自定义Forms验证过程(使用IHttpModule是常用做法):
1.) 解决用户点击”登录按钮”后的票据生成和保存问题.
//LoginPage.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="LoginPage.aspx.cs" Inherits="LoginPage" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:TextBox ID="txt_username" runat="server"></asp:TextBox><br />
<asp:TextBox ID="txt_userpass" runat="server"></asp:TextBox><br />
<asp:LinkButton ID="lbtn_login" runat="server" onclick="lbtn_login_Click">登陆</asp:LinkButton>
</div>
</form>
</body>
</html>
//Loginpage.aspx.cs
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
public partial class LoginPage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void lbtn_login_Click(object sender, EventArgs e)
{
if (IsValid)
{
string name = this.txt_username.Text.Trim();
string pass = this.txt_userpass.Text.Trim();
//获得用户输入密码的散列码:
byte[] buffer;
buffer = System.Text.Encoding.GetEncoding("utf-8").GetBytes(pass);
buffer = System.Security.Cryptography.MD5CryptoServiceProvider.Create().ComputeHash(buffer);
string password = BitConverter.ToString(buffer).Replace("-", "");
#region 这里为简单, 直接通过配置文件读取账户名和密码. 使用Authenticate()方法验证
if (FormsAuthentication.Authenticate(name, password)) //是否通过验证
{
//FormsAuthentication.RedirectFromLoginPage(name, false);
//验证通过, 创建票据
System.Web.Security.FormsAuthenticationTicket ticket = new System.Web.Security.FormsAuthenticationTicket(2, name, DateTime.Now, DateTime.Now.AddDays(7), true, string.Empty);
//在加入到Cookie之前, 先进行加密
string cookiestring = System.Web.Security.FormsAuthentication.Encrypt(ticket);
//创建Cookie, 可直接使用任意串做Cookie名. 这里利用FormsAuthentication类从配置文件里读取
HttpCookie cookie = new HttpCookie(System.Web.Security.FormsAuthentication.FormsCookieName);
cookie.Value = cookiestring;
cookie.Expires = DateTime.Now.AddDays(3);
//将Cookie发送到浏览器
this.Response.Cookies.Add(cookie);
//this.Response.SetCookie(cookie); //更新Cookie
this.Response.Redirect("default.aspx");
}
#endregion
#region 这个验证过程也可以通过调用BLL层、DAL层访问数据库来完成(常见做法)
//这里忽略访问数据库的过程
#endregion
}
}
}
2.) 自定义类CustomAuthentication.cs实现IHttpModule接口, 在IHttpModule的AuthenticateRequest事件中, 编写自定义的认证处理过程并创建程序中的用户标识. 当然, 最后千万不能忘了要在配置文件的<httpModules>配置节中注册我们自定义的类CustomAuthentication. 这样的做的好处: 程序员将从状态管理的代码中解放出来, 我们只要在页面调用IsAuthenticate()方法即可, 也不需要手动的维护跳转的工作, 更方便的是我们可以随时通过页面的User对象获得用户信息(要注意: 这里的用户信息, 是在程序内的表示, 继承自IPrincipal和IIdentity两个接口).
//CustomAuthentication.cs
using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
/// <summary>
/// Summary description for CustomAuthentication
/// </summary>
public class CustomAuthentication : IHttpModule
{
#region IHttpModule Members
public void Dispose()
{
}
public void Init(HttpApplication application) //这里的上下文, 容易和HttpContext弄混, 改为application
{
//注册AuthenticateRequest事件的处理器
application.AuthenticateRequest += new EventHandler(application_AuthenticateRequest);
}
void application_AuthenticateRequest(object sender, EventArgs e)
{
//取得appliation中cookie对象, 并从中取得用户标识; sender其实就是application(通过参数传递application的引用)
HttpApplication application = sender as HttpApplication;
//两种从application中取Cookie的方式均可
HttpCookie cookie = application.Request.Cookies[FormsAuthentication.FormsCookieName]; //从配置文件中取, 也可用固定的串hardcode进程序中
//HttpCookie cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName]; //从上下文对象中取Request中的Cookie
string username = string.Empty;
if (cookie != null)
{
//从Cookie中取加密后的票据, 并反序列化得到票据
string cookiestring = cookie.Value;
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookiestring);
//从票据中取用户名
username = ticket.Name;
}
//注意: username为空表示验证失败
//用户信息需要编程程序中的标识方式, 可以GenericPrincipal、WindowsPrincipal, Forms常用GenericPrincipal
System.Security.Principal.GenericIdentity identity = new System.Security.Principal.GenericIdentity(username);
System.Security.Principal.GenericPrincipal principal = new System.Security.Principal.GenericPrincipal(identity, new string[] { "Admin","Users" }); //用户的名称和组信息
//为了在程序中方便获得用户信息, 将用户信息保存到HttpContext中, 进行状态管理
HttpContext hc = application.Context;
//hc.Items.Add("User", principal); //可以放到集合中, 也可以直接放到User中
hc.User = principal;
}
#endregion
}
//web.config
<httpModules>
<add name="CustomPrincipal" type="CustomAuthentication"/>
</httpModules>
3.) 上述步骤完成后, 我们就可以在其他的后台页面中, 通过User或Identity获得用户信息. 即在页面中使用用户标识
//default.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lbl_msg" runat="server" Text="Label"></asp:Label><br />
<asp:LinkButton ID="logout" runat="server" onclick="logout_Click">注销</asp:LinkButton>
</div>
</form>
</body>
</html>
//default.aspx.cs
using System;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
this.lbl_msg.Text = this.User.Identity.IsAuthenticated ? this.User.Identity.Name + " 已登录! " : " 您还未登录! ";
}
protected void logout_Click(object sender, EventArgs e)
{
//创建新Cookie对象, 并修改过期时间
HttpCookie cookie = new HttpCookie(System.Web.Security.FormsAuthentication.FormsCookieName);
cookie.Expires = DateTime.Now.AddDays(-7);
this.Response.Cookies.Add(cookie);
//this.Response.Redirect("LoginPage.aspx");
this.Response.Redirect(this.Request.Path); //重定请求刚才的资源
}
}
//web.config
<authentication mode="Forms">
<forms name="Cait" loginUrl="LoginPage.aspx" defaultUrl="Default.aspx" protection="All">
<credentials passwordFormat="Clear">
<user name="Admin" password="0192023A7BBD73250516F069DF18B500" />
</credentials>
</forms>
</authentication>
<authorization>
<deny users="?"/>
</authorization>
最后, 关于授权:
Asp.net的授权机制为我们提供了一种特别方便的组织资源的方式: 由于配置文件是有继承关系的, 所以我们可以将某类资源放在同一个文件夹中, 再配上一个配置文件用来规定有哪些用户可以访问, 这样就可以实现对资源的分层次管理的要求.
虽然也可以在应用程序处理管道的AuthorizeRequest事件中, 进行授权处理. 但实际上, 我们很少很少哪么做. 关于授权有几个事情需要留意下:
1.) 默认登录页面不受配置文件中的规则限制.
2.) 配置元素中只有两种行为, allow 和 deny; 而每种行为都有3个属性可以使用, 分别是: users、roles、verbs. 前两个不用解释, 在AuthenticateRequest的事件中, 我们创建的用户对象就包含这些内容. Verbs用来分隔Http协议的请求方法, 可以有多个(如: verbs = “GET,POST,HEAD,DEBUG”), 注意大小写一定要正确.
3.) 两个特殊的符号, “?”表示匿名用户, “*”表示所有用户.
4.)配置规则是从上到下依次生效的, 比如: 你可以先定义规则<allow users=”AA”>, 紧跟着第二行定义<deny user=”*”>; 哪么上边的优先执行, 最终结果就是只允许AA访问.
5.) 最后一个事情, 就是关于资源的管理有两种配置节. 一种是<authorization>, 这种适合大范围限制某些资源, 如整个文件夹; 另外一种是<location>, 这种适合针对某个资源进行配置, 用于较小的范围.
下面是两个例子:
<authorization>
<allow users="AA"/>
<deny users="*"/>
</authorization>
<location path="default.aspx">
<system.web>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
</location>
额外多说几句: 配置文件中的<location>配置节, 主要用来添加相同的配置节元素. 比如: 如果外边不加<location>配置节, 则默认只允许有一个<system.web>节, 但是外边套一个<location>就允许有两个<system.web>配置节了.