您现在的位置是:网站首页> 编程资料编程资料

Asp.Net实现FORM认证的一些使用技巧(必看篇)_实用技巧_

2023-05-24 394人已围观

简介 Asp.Net实现FORM认证的一些使用技巧(必看篇)_实用技巧_

最近因为项目代码重构需要重新整理用户登录和权限控制的部分,现有的代码大体是参照了.NET的FORM认证,并结合了PORTAL KITS的登录控制,代码比较啰嗦,可维护性比较差。于是有了以下的几个需求(大多数系统应该都会碰到):

1.用.NET自带的FORM认证来实现安全登录

2.登录后需要记录登录用户的基本信息,方便所有页面调用

3.记录本机登录状态,短时间关闭窗口后不用重新登录

4.权限控制和代码的文件夹结构相呼应,即按角色允许访问不同的目录

5.权限控制有可能需要细化到每一个页面,即按角色允许访问不同的页面

6.以上的部分尽量自己少写代码,用自带的类库和机制实现

第一步:准备工作

先准备一个名为Test的WEB项目,包含:

Default.aspx,默认页,随便显示一些信息,

Login.aspx,登录页,上面放两个文本框,用来输入用户名和密码,一个登录按钮,一个指向Register.aspx的超链,

Register.aspx,用户注册页,注册用户信息,随便放一点文本框,主要是模拟一下注册,不用真正实现,

Web.config,配置页面。

注册页与登录页在同一目录的机妙后面会说。

第二步:Web.config文件的修改

1、打开Web.config文件,找到authentication节,将其改为如下:

配置节属性的具体意义和其他没有加入的属性网上到处都有查。这里注意一下的是authentication节和authorization节,两个单词很相似,但却不是同一个单词,每个节下面的内容也不能写到一起。

其中,authorization节中的“allow”表示允许的意思,“*”表示所有用户;而“deny”表示拒绝的意思;“?”表示匿名用户;此处加入后,则代表根目录下的所有文件和所有的子目录都不能匿名访问,Login.aspx 页面除外。

2、Web.config中,Location节的应用

做了上面的配置之后,我们会发现,在没登录的情况下,用浏览器打开Default.aspx会自动转到Login.aspx,同理Register.aspx页面也会如此。问题:注册用户怎么可能在登录后才能访问呢?

那么我们就得说了,当注册页与登录页在同一目录,为了达到不用登录就能访问注册页的目的,我们就得对访问限制的Web.config配置处理一下。

方法一 :注册页与登录页放在不的目录内

我们在根目录添加一个文件夹Pub,将Register.aspx移动到此文件夹里,此时仍不能访问,需要在文件夹内添加一个Web.config文件,加入:

此处,即说明此目录下的所有文件,允许所有人访问。

关于 Web.config 作用范围的说明:

• Web.config 的设置将作用于所在目录的所有文件及其子目录下的所有东东(继承:子随父姓)

• 子目录下的 Web.config 设置将覆盖由父目录继承下来的设置(覆盖:县官不如现管)

• 也就是,属性设置由最深一层的目录里的Web.config决定;如果子目录里没有Web.config文件,则由离它最近的父目录里的Web.config决定

方法二:仍然保持注册页和登录页在同一目录下

只需要在根目录下的Web.config 中加入以下一段:

通过location节的path属性的值指定Register.aspx页面,以及下面authorization节的设置,说明了Register.aspx页面是允许被所有人访问。

注意:

location节应加在原有的system.web节的外面,包含在configuration节内,和system.web节是同级的。  

当根目录下,有多个页面不需要登录就可以访问时,可以设置多个location节,修改对应path属性值指向的页面就可以了。

另外,path属性的值也可以指定目录,用来指定该目录的访问限制。通过修改authorization节的内容来限定访问权限。详细的设置,后面会提到。

第三步:实现登录的代码

1、普通的代码实现

方法一:

如果forms节中设置了“defaultUrl”的属性,也就是登录后默认转向的页面,则可以用如下的方法:

 private void Btn_Login_Click(object sender, System.EventArgs e) { if(this.Txt_UserName.Text=="Admin" && this.Txt_Password.Text=="123456") { FormsAuthentication.RedirectFromLoginPage(this.Txt_UserName.Text,false); } } 

此处只是简单模拟了一下登录的验证过程,RedirectFromLoginPage方法能发送验证票据验证Cookie(如何进行可以用Reflector去查看源代码),返回请求页面,即“从哪来就打哪去”。比如:用户没登录前直接在 IE 地址栏输入 http://localhost/Test/Default.aspx ,那么该用户将看到的是 Login.aspx?ReturnUrl=Default.aspx ,输入用户名与密码登录成功后,系统将根据“ReturnUrl”的值,返回相应的页面;如果没有“ReturnUrl”,则按照“defaultUrl”的属性自动转向。

方法二:

 private void Btn_Login_Click(object sender, System.EventArgs e) { if(this.Txt_UserName.Text=="Admin" && this.Txt_Password.Text=="123456") { FormsAuthentication.SetAuthCookie(this.Txt_UserName.Text,false); Response.Redirect("Default.aspx"); } } 

此处是分两步走:通过验证后就直接发放 Cookie ,跳转页面将由程序员自行指定,无需“defaultUrl”设置。此方法对于程序员来说,更灵活。  

2、手工实现需要记录用户登录信息的情况

当我们需要记录用户登录的信息,不单单只是一个ID还需要更多属性的时候,一般都用一个类存储到Session或Cookie实现,然后做一个基类页,在基类页中设置属性来读取Session或Cookie。

Session实际也和支不支持Cookie有关,且存在服务器,多少会占用服务器端资源。因此这里还是考虑用Cookie实现。那么在RedirectFromLoginPage方法或SetAuthCookie方法已经设置了验证票据并设置了Cookie,我们能不能把用户登录信息也存储到这个默认的Cookie里呢?答案是能。

首先,我们在项目里添加AppCode目录,增加一个UserInfo的类,用以简单模拟用户登录信息。代码如下:

 [Serializable] public class UserInfo { //用户登录信息 private int _nId; private string _sRealName; private string _sName; private string _sPassword; private string _sRoles; public int Id { get { return this._nId; } set { this._nId = value; } } public string RealName { get { return this._sRealName; } set { this._sRealName = value; } } public string Name { get { return this._sName; } set { this._sName = value; } } public string Password { get { return this._sPassword; } set { this._sPassword = value; } } public string Roles { get { return this._sRoles; } set { this._sRoles = value; } } public UserInfo() { } } 

需要注意, 类的属性中一定要加[Serializable],表示类可以序列化。

Forms验证在内部的机制是,把用户数据加密后保存在一个基于cookie的票据FormsAuthenticationTicket中,通过RedirectFromLoginPage方法或SetAuthCookie方法就已经实现了Ticket和Cookie的设置,也就是设置了Context.User的值,Context.User在取值和判断是否经过验证的时候很有用处。Cookie的属性是在Web.config的<forms name=".ASPXAUTH" loginUrl="Login.aspx" protection="All" path="/" timeout="20"/>中设置的。因为是经过特殊加密的,所以应该来说是比较安全的。

而.net除了用这个票据存放自己的信息外,还留了一个地给用户自由支配,这就是现在要说的Ticket的UserData。 UserData用来存储string类型的信息,并且也享受Forms验证提供的加密保护,当我们需要这些信息时,也可以通过简单的Ticket的 UserData属性得到,兼顾了安全性和易用性,用来保存一些必须的敏感信息还是很有用的。我们就准备将用户的登录信息记录在UserData中,代码如下:

 protected void Button1_Click(object sender, EventArgs e) { if (this.TextBox1.Text == "Admin" && this.TextBox2.Text == "123456") { // 加密UserInfo UserInfo user = new UserInfo(); user.Id = 1; user.Name = this.TextBox1.Text; user.Password = this.TextBox2.Text; user.RealName = "系统管理员"; user.Roles = "Administrators,Users"; string strUser = Serialize.Encrypt(user); // 设置Ticket信息 FormsAuthenticationTicket ticket = new FormsAuthenticationTicket( 1, user.Name, DateTime.Now, DateTime.Now.AddMinutes(20), false, strUser); // 加密验证票据 string strTicket = FormsAuthentication.Encrypt(ticket); // 使用新userdata保存cookie HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, strTicket); cookie.Expires = ticket.Expiration; this.Response.Cookies.Add(cookie); this.Response.Redirect("Default.aspx"); } }

上面的代码,实际上类似于手工实现了SetAuthCookie方法的过程。

首先,模拟实现登录,我们手动设置了一个UserInfo的对象,string strUser = Serialize.Encrypt<UserInfo>(user) 是将对象序列化成字符串的一个方法。

  然后,生成一个FormsAuthenticationTicket票据。此处用到的FormsAuthenticationTicket构造函数的重载方法的签名解释

 public FormsAuthenticationTicket(     int version, //版本号     string name, //与身份验证票关联的用户名     DateTime issueDate, //票据的发出时间     DateTime expiration,//票据的到期日期     bool isPersistent, //票据是否存储在持久的 Cookie 中,是为 true;否则为 false     string userData //票据中存储的用户定义数据 );

再后,string strTicket = FormsAuthentication.Encrypt(ticket) 将票据加密成字符创

最后,HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, strTicket) 生成Cookie。

FormsAuthentication.FormsCookieName获取的就是Web.config中配置的Cookie名称,也就是默认验证时产生的Cookie。cookie.Expires = ticket.Expiration 将票据的过期时间和Cookie的过期时间做了同步,也就避免了两者不同所产生的矛盾。这样,验证票据生成了,存储到默认配置的Cookie中,也就是类似实现了一个SetAuthCookie方法的过程。通过Context.User就能获取票据的相关信息了。

3、获取信息

为了在其他登录后的页面比较简单的获取登录用户信息,我们先生成一个基类页面。在AppCode中新增LoginBasePage类,代码如下:

 public class LoginBasePage : Page { protected UserInfo LoginUser { get { string strUser = ((FormsIdentity)this.Context.User.Identity).Ticket.UserData; return Serialize.Decrypt(strUser); } } public LoginBasePage() { // // TODO: 在此处添加构造函数逻辑 // } }

LoginBasePage : Page,基类页要继承Page,成为所有登录以后的页面的基类。

属性protected UserInfo LoginUser{ get;}用来访问登录信息。将Co

-六神源码网