Forms身份验证的配置

authentication — 认证; 身份验证; 证明,鉴定; 密押;

authorization — 授权,批准; 批准(或授权)的证书;

<authentication mode="Windows|Forms|Passport|None">
    <forms name=".ASPXAUTH" loginUrl="Login.aspx" protection="All" path="/" timeout="20"/>
</authentication>
<authorization>
    <deny users="?"></deny>
</authorization>

其中 authentication 是节点名,mode是模式,上述代码表示 采用windows身份验证,那么此时身份验证将交给iis处理

 

属性 选项 说明
mode   控制应用程序的默认身份验证模式。
  Windows 将 Windows 验证指定为默认的身份验证模式。当使用以下任意形式的 Microsoft Internet 信息服务 (IIS) 身份验证时使用该模式:基本、简要、集成的 Windows 验证 (NTLM/Kerberos) 或证书。
  Forms 将 ASP.NET 基于窗体的身份验证指定为默认的身份验证模式。
  Passport 将 Microsoft Passport 身份验证指定为默认的身份验证模式。
  None 不指定任何身份验证。只有匿名用户是预期的或者应用程序可以处理事件以提供其自身的身份验证。

 

a、使mode为 Forms 则表示是用Forms身份验证

b、defaultUrl ="default.aspx" 表示登陆后转向的默认页面是default.aspx

c、loginUrl="login.aspx" 表示登陆页面是login.aspx

d、protection="All" 表示 保护所有页面,但不能保护html页面,只能保护aspx页面。

e、节点中 deny users="?" 表示拒绝所有匿名用户,也就是说必须通过验证的用户才可以跳转到默认页或由程序指定一个跳转页,其中 "?" 表示匿名用户,也可以设置为 "*" 表示所有用户,或则指定一个用户名。

f、timeout 表示有效时间。

 

 

基于窗体验证的授权机制

 

<authorization>

 

     <allow users="逗号分割的用户列表"

 

            roles="逗号分割的角色列表"/>

 

     <deny  users="逗号分割的用户列表"

 

            roles="逗号分割的角色列表"/>

 

</authorization>

 

(1)“*”通常用在allow元素钟,表式允许所有用户访问

 

(2)“?”通常用在deny中,表式禁止未经过身份验证的用户访问

 

 登陆按钮事件必须使用如下代码才能跳转
            FormsAuthentication.RedirectfromLoginPage("luby", false);
引用的命名空间是 System.Web.Security; RedirectfromLoginPage表示将经过身份验证的用户重定向到最初请求的页面,第一参数表示名称,此名称今后在本站点的任何页面都可以用 User.Identity.Name 来获取到,第二个参数表示是否要持久化cookie,也就是说是否下次不需要输入密码验证就可以自动登陆,直到用户主动退出。
退出按钮必须使用如下代码,命名空间同样是 System.Web.Security;
FormsAuthentication.SignOut();

 

 

web.config中如何只针对一个子目录设置验证   

 

  在子目录"usr"下面,新建一个web.config文件:   
  <configuration>   
          <system.web>   
                  <authorization>   
                          <deny   users="?"/>     
                  </authorization>   
          </system.web   >   
  </configuration>

也可以不用在每一个子目录中新那一个web.config文件,在根目录下的web.config也也可以设置,象这样:   
    
  <configuration>   
  <system.web>   
  <authentication   mode="Forms"   >   
  <forms   name="WebdiyerLoginForm"   loginUrl="Login.aspx"></forms>   
  </authentication></system.web>   
    
  <location   path="test">   
  <system.web>   
  <authorization>   
  <deny   users="?"/>   
  </authorization>   
  </system.web>   
  </location>   
    
  </configuration>   
  其中的location配置节即可用来控制某个子目录或文件的访问,path值即是要限制访问的文件名(带后缀)或文件夹名。

 

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

  • Web.config 的设置将作用于所在目录的所有文件及其子目录下的所有东东(继承:子随父姓) 
  • 子目录下的 Web.config 设置将覆盖由父目录继承下来的设置(覆盖:县官不如现管)
  • 也就是,属性设置由最深一层的目录里的Web.config决定;如果子目录里没有Web.config文件,则由离它最近的父目录里的Web.config决定

 

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”的属性自动转向。

 

 

if(this.Txt_UserName.Text=="Admin" && this.Txt_Password.Text=="123456"
   { 
      FormsAuthentication.SetAuthCookie(this.Txt_UserName.Text,false); 
      Response.Redirect("Default.aspx"); 
   }

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

 

那么在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<UserInfo>(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 //票据中存储的用户定义数据
);
其中,name的设置与Context.User.Identity.Name对应,且大小写敏感,也与将来的权限控制相关,赋值的时候需要特别注意。另外,票据的到期日期和Web.config中设置的Cookie的到期日期不是同一个概念,如果分不清,请到网上去搜索,如果实在不想在这上下功夫,后面会有处理的方法。

  再后,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<UserInfo>(strUser);               
        }
    }

    public LoginBasePage()
    {
        //
        // TODO: 在此处添加构造函数逻辑
        //
    }
}

 

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

  属性protected UserInfo LoginUser{ get;}用来访问登录信息。将Context.User.Identity强制转换为FormsIdentity类的对象,通过访问Ticket属性的UserData属性,获得被序列化后的对象的字符串,最后用方法Serialize.Decrypt<UserInfo>(strUser)将字符串反序列化成对象后再返回UserInfo类型的对象。

 

 

 

  我们只需要将Default页面的后台代码改为public partial class _Default : LoginBasePage,就可以通过this.LoginUser来访问用户登录信息了。

实现不同目录的权限控制

首先,我们在根目录增加一个文件夹ManageAdmin,在此文件夹内增加页面UserInfo.aspx,页面内放几个Label用来展现登录用户信息。

  然后,再增加一个Web.config文件,配置内容如下:

<configuration>
    <appSettings/>
    <connectionStrings/>
    <system.web>
        <authorization>
            <allow users="Admin"></allow>
            <deny users="*"></deny>
        </authorization>
    </system.web>
</configuration>

配置中说明只允许“Admin”用户访问,禁止其他所有用户访问。

  这里,特别要注意的是,FormsAuthenticationTicket票据的name属性的赋值,一定要和<allow users="Admin"></allow>设置的用户想对应,且大小写敏感。如果要设置允许多个用户访问,则用“,”隔开,例如<allow users="Admin,User1"></allow>。

 

  不同的目录,设置不同的允许访问的用户,就可以对所有目录进行访问控制了。

 

实现不同目录的按角色的权限控制

<configuration>
  <appSettings/>
  <connectionStrings/>
  <system.web>
    <authorization>
      <allow roles="Users"></allow>
      <deny users="*"></deny>
    </authorization>
  </system.web>
</configuration>
再将文件夹ManageAdmin下的Web.config文件的<allow users="Admin"></allow>改成<allow roles="Administrators"></allow>

1、注意,我们在模拟用户信息的时候,有这么一句,user.Roles = "Administrators,Users";也就是用户Admin具备两种角色

2、为模拟Users组的用户登录,我们再添加如下代码:

 

if (this.TextBox1.Text == "User1" && this.TextBox2.Text == "111111")
{
      // 加密UserInfo
      UserInfo user = new UserInfo();
      user.Id = 2;
      user.Name = this.TextBox1.Text;
      user.Password = this.TextBox2.Text;
      user.RealName = "普通用户1";
      user.Roles = "Users";
      string strUser = Serialize.Encrypt<UserInfo>(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");
 }
 
这样,我们登录时,输入“Admin”和“User1”的时候,就可以模拟不同角色的用户登录了。
 
3、Forms基于角色的验证的内部机制是,将角色的属性也设置到了Context.User中,这里也需要手工代码处理一下。        

  首先,为了支持基于角色的验证,我们每进入一个页面都需要将角色信息设置到Context.User中,那么最好的办法就是在Global.asax 文件中的Application_AuthenticateRequest方法中设置。

  Application_AuthenticateRequest方法,是在每次验证请求时触发,它与另外一个方法 Application_BeginRequest的区别就在于,Application_AuthenticateRequest方法内,能够访问 Context.User.Identity,而Application_BeginRequest则无法访问。

  我们在根目录添加一个Global.asax 文件,增加如下代码:

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
  {
    if (this.Context.User != null)
    {
      if (this.Context.User.Identity.IsAuthenticated)
      {
        if (this.Context.User.Identity is FormsIdentity)
        {
          string strUser = ((FormsIdentity)this.Context.User.Identity).Ticket.UserData;
 
          string[] roles = Serialize.Decrypt<UserInfo>(strUser).Roles.Split(',');
 
          this.Context.User = new GenericPrincipal(this.Context.User.Identity, roles);
        }
      }
    }
  }
 
此处,主要代码就是将Context.User.Identity强制转换为FormsIdentity类的对象,通过访问Ticket属性的UserData属性,获得被序列化后的对象的字符串,最后用方法Serialize.Decrypt<UserInfo>(strUser)将字符串反序列化成对象,再将UserInfo对象的Roles属性以“,”为分隔符分隔成角色数组,再用Context.User.Identity和角色数组生成一个新的GenericPrincipal对象,赋值给Context.User ,则Context.User 为已经设置好角色的验证对象。

  按照我们的设置,Admin用户能访问两个目录,而User1用户,则只能访问ManageUsers一个目录。

 

集中管理Web.config文件

  目录的访问权限控制,是按用户还是按角色,一般由具体业务决定。

 

  但是,随着目录的增多,每个目录下都存在一个Web.config文件,管理起来特别不方便。

 

  通过上面提到过的location节的path属性,我们可以实现Web.config配置的统一管理。我们可以将各个文件或目录的配置都放置在根目录的Web.config文件内,代码如下:

<configuration>
    <appSettings/>
    <connectionStrings/>

    <location path ="Register.aspx">
        <system.web>
            <authorization>
                <allow users="*"/>
            </authorization>
        </system.web>
    </location>

        <location path ="ManageAdmin">
        <system.web>
            <authorization>
                <allow roles="Administrators"></allow>
                    <deny users="*"></deny>
            </authorization>
        </system.web>
    </location>

       <location path ="ManageUsers">
        <system.web>
            <authorization>
                <allow roles="Users"></allow>
                    <deny users="*"></deny>
            </authorization>
        </system.web>
    </location>

        <system.web>
        <!-- 这里放置原来根目录 Web.config 的内容,就不列出来了 -->
         </system.web>

</configuration>

 

 
 
 
 
 
 
 
 
 
 

 

 

 

 

 

 

 

 

 

 

 

 

posted on 2017-08-04 13:01  土金豪  阅读(796)  评论(0编辑  收藏  举报