注:不会涉及ASP.NET的登录系列控件以及membership的相关话题, 我只想用比较原始的方式来说明在ASP.NET中是如何实现身份认证的过程。 

  ASP.NET身份认证基础

  在开始今天的博客之前,我想有二个最基础的问题首先要明确:

  1. 如何判断当前请求是一个已登录用户发起的?

  2. 如何获取当前登录用户的登录名?

  在标准的ASP.NET身份认证方式中,上面二个问题的答案是:

  1. 如果Request.IsAuthenticated为true,则表示是一个已登录用户。

  2. 如果是一个已登录用户,访问HttpContext.User.Identity.Name可获取登录名(都是实例属性)。

 ASP.NET身份认证过程

  在ASP.NET中,整个身份认证的过程其实可分为二个阶段:认证与授权。

  1. 认证阶段:识别当前请求的用户是不是一个可识别(的已登录)用户。

  2. 授权阶段:是否允许当前请求访问指定的资源。

  这二个阶段在ASP.NET管线中用AuthenticateRequest和AuthorizeRequest事件来表示。

  在认证阶段,ASP.NET会检查当前请求,根据web.config设置的认证方式,尝试构造HttpContext.User对象供我们在后续的处理中使用。 在授权阶段,会检查当前请求所访问的资源是否允许访问,因为有些受保护的页面资源可能要求特定的用户或者用户组才能访问。 所以,即使是一个已登录用户,也有可能会不能访问某些页面。 当发现用户不能访问某个页面资源时,ASP.NET会将请求重定向到登录页面。

  受保护的页面与登录页面我们都可以在web.config中指定,具体方法可参考后文。

  在ASP.NET中,Forms认证是由FormsAuthenticationModule实现的,URL的授权检查是由UrlAuthorizationModule实现的。

    1. 登录:调用FormsAuthentication.SetAuthCookie()方法,传递一个登录名即可。

  2. 注销:调用FormsAuthentication.SignOut()方法。

 保护受限制的页面

  为了保护受限制的页面的访问,ASP.NET提供了一种简单的方式: 可以在web.config中指定受限资源允许哪些用户或者用户组(角色)的访问,也可以设置为禁止访问。

  比如,网站有一个页面:MyInfo.aspx,它要求访问这个页面的访问者必须是一个已登录用户,那么可以在web.config中这样配置:

<location path="MyInfo.aspx">
   <system.web>
     <authorization>
        <deny users="?"/>
     </authorization>
   </system.web>
</location>

为了方便,我可能会将一些管理相关的多个页面放在Admin目录中,显然这些页面只允许Admin用户组的成员才可以访问。 对于这种情况,我们可以直接针对一个目录设置访问规则:

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

这样就不必一个一个页面单独设置了,还可以在目录中创建一个web.config来指定目录的访问规则。在前面的示例中,有一点要特别注意的是:

1. allow和deny之间的顺序一定不能写错了,UrlAuthorizationModule将按这个顺序依次判断。

2. 如果某个资源只允许某类用户访问,那么最后的一条规则一定是在allow和deny的配置中,我们可以在一条规则中指定多个用户:

  1. 使用users属性,值为逗号分隔的用户名列表。

  2. 使用roles属性,值为逗号分隔的角色列表。

  3. 问号 (?) 表示匿名用户。

  4. 星号 (*) 表示所有用户。

在ASP.NET内部,当发现是在访问登录面时,会设置HttpContext.SkipAuthorization = true (其实是一个内部调用), 这样的设置会告诉后面的授权检查模块:跳过这次请求的授权检查。因此,登录页总是允许所有用户访问,但是登录页所引用的CSS文件以及JS文件(页面在访问CSS, JS文件时,其实是被重定向到登录页面了) 是在另外的请求中发生的,那些请求并不会要跳过授权模块的检查。

  为了解决登录页不能正确显示的问题,我们可以这样处理:

  1. 在网站根目录中的web.config中设置登录页所引用的JS, CSS文件都允许匿名访问。

  2. 也可以直接针对JS, CSS目录设置为允许匿名用户访问。

  3. 还可以在CSS, JS目录中创建一个web.config文件来配置对应目录的授权规则。可参考以下web.config文件:

<?xml version="1.0"?>
<configuration>
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</configuration>

注意:在IIS中看到的情况就和在Visual Studio中看到的结果就不一样了。因为,像js, css, image这类文件属于静态资源文件,IIS能直接处理,不需要交给ASP.NET来响应,因此就不会发生授权检查失败, 所以,如果这类网站部署在IIS中,看到的结果又是正常的。

认识Forms身份认证

HTTP是一个无状态的协议, 在开发WEB应用程序时,我们通常会使用Cookie来保存一些简单的数据供服务端维持必要的状态。以下是我用FireFox所看到的Cookie列表:

  

  这个名字:LoginCookieName,是在web.config中指定的:

<authentication mode="Forms" >
<forms cookieless="UseCookies" name="LoginCookieName" loginUrl="~/Default.aspx"></forms>
</authentication>

理解Forms身份认证

为了实现安全性,ASP.NET采用【Forms身份验证凭据】(即FormsAuthenticationTicket对象)来表示一个Forms登录用户, 加密与解密由FormsAuthentication的Encrypt与Decrypt的方法来实现。

  用户登录的过程大致是这样的:

  1. 检查用户提交的登录名和密码是否正确。

  2. 根据登录名创建一个FormsAuthenticationTicket对象。

  3. 调用FormsAuthentication.Encrypt()加密。

  4. 根据加密结果创建登录Cookie,并写入Response。

  在登录验证结束后,一般会产生重定向操作, 那么后面的每次请求将带上前面产生的加密Cookie,供服务器来验证每次请求的登录状态。

每次请求时的(认证)处理过程如下:

  1. FormsAuthenticationModule尝试读取登录Cookie。

  2. 从Cookie中解析出FormsAuthenticationTicket对象。过期的对象将被忽略。

  3. 根据FormsAuthenticationTicket对象构造FormsIdentity对象并设置HttpContext.User

  4. UrlAuthorizationModule执行授权检查。

  在登录与认证的实现中,FormsAuthenticationTicket和FormsAuthentication是二个核心的类型, 前者可以认为是一个数据结构,后者可认为是处理前者的工具类。

UrlAuthorizationModule是一个授权检查模块,其实它与登录认证的关系较为独立, 因此,如果我们不使用这种基于用户名与用户组的授权检查,也可以禁用这个模块

  由于Cookie本身有过期的特点,然而为了安全,FormsAuthenticationTicket也支持过期策略, 不过,ASP.NET的默认设置支持FormsAuthenticationTicket的可调过期行为,即:slidingExpiration=true 。 这二者任何一个过期时,都将导致登录状态无效。

  FormsAuthenticationTicket的可调过期的主要判断逻辑由FormsAuthentication.RenewTicketIfOld方法实现,代码如下:

public static FormsAuthenticationTicket RenewTicketIfOld(FormsAuthenticationTicket tOld)
{
// 这段代码是意思是:当指定的超时时间逝去大半时将更新FormsAuthenticationTicket对象。 if( tOld == null )
return null; DateTime now = DateTime.Now;
TimeSpan span = (TimeSpan)(now - tOld.IssueDate);
TimeSpan span2 = (TimeSpan)(tOld.Expiration - now);
if( span2 > span )
return tOld; return new FormsAuthenticationTicket(tOld.Version, tOld.Name,
now, now + (tOld.Expiration - tOld.IssueDate),
tOld.IsPersistent, tOld.UserData, tOld.CookiePath);
}

在多台服务器之间使用Forms身份认证(Passport单点身份验证)

  默认情况下,ASP.NET 生成随机密钥并将其存储在本地安全机构 (LSA) 中, 因此,当需要在多台机器之间使用Forms身份认证时,就不能再使用随机生成密钥的方式,需要我们手工指定,保证每台机器的密钥是一致的。

  用于Forms身份认证的密钥可以在web.config的machineKey配置节中指定,我们还可以指定加密解密算法:

<machineKey 
decryption="Auto" [Auto | DES | 3DES | AES]
decryptionKey="AutoGenerate,IsolateApps" [String]
/>

Passport单点身份验证:(<machineKey validationKey="5029E82E1779497186D46F83D78FAD07" decryptionKey="82B8397DB5B4443FB035083EB662CD98"

validation="SHA1" decryption="Auto" />)

1:获取机器key 生成密钥

2:在要实现单点登陆的项目根web.config中添加密钥;

a) 两个项目Web.cinfig的<machineKey> 节点确保以下几个字段完全一样:validationKey 、decryptionKey 、validation 
b) 两个项目的 Cookie 名称必须相同,也就是 <forms> 中的 name 属性,这里我们把它统一为 name ="UserLogin" 
c) 注意区分大小写 
d) 登陆页面整合到统一登陆点登陆   比如:loginUrl="www.wf.com/login.aspx", 在登陆页面发放验证票

在客户端程序中访问受限页面

  有时我们需要用代码访问某些页面,比如:希望用代码测试服务端的响应。

  如果是简单的页面,或者页面允许所有客户端访问,这样不会有问题, 但是,如果此时我们要访问的页面是一个受限页面,那么就必须也要像人工操作那样: 先访问登录页面,提交登录数据,获取服务端生成的登录Cookie, 接下来才能去访问其它的受限页面(但要带上登录Cookie)。

在前面的示例中,我已在web.config为MyInfo.aspx设置过禁止匿名访问,如果我用下面的代码去调用:

private static readonly string MyInfoPageUrl = "http://localhost:51855/MyInfo.aspx";

static void Main(string[] args)
{
// 这个调用得到的结果其实是default.aspx页面的输出,并非MyInfo.aspx
HttpWebRequest request = MyHttpClient.CreateHttpWebRequest(MyInfoPageUrl);
string html = MyHttpClient.GetResponseText(request); if( html.IndexOf("<span>Fish</span>") > )
Console.WriteLine("调用成功。");
else
Console.WriteLine("页面结果不符合预期。");
}

  此时,输出的结果将会是:页面结果不符合预期。

 如果我用下面的代码:

private static readonly string LoginUrl = "http://localhost:51855/default.aspx";
private static readonly string MyInfoPageUrl = "http://localhost:51855/MyInfo.aspx"; static void Main(string[] args)
{
// 创建一个CookieContainer实例,供多次请求之间共享Cookie
CookieContainer cookieContainer = new CookieContainer(); // 首先去登录页面登录
MyHttpClient.HttpPost(LoginUrl, "NormalLogin=aa&loginName=Fish", cookieContainer); // 此时cookieContainer已经包含了服务端生成的登录Cookie // 再去访问要请求的页面。
string html = MyHttpClient.HttpGet(MyInfoPageUrl, cookieContainer); if( html.IndexOf("<span>Fish</span>") > )
Console.WriteLine("调用成功。");
else
Console.WriteLine("页面结果不符合预期。"); // 如果还要访问其它的受限页面,可以继续调用。
}

  此时,输出的结果将会是:调用成功。

  说明:在改进的版本中,我首先创建一个CookieContainer实例, 它可以在HTTP调用过程中接收服务器产生的Cookie,并能在发送HTTP请求时将已经保存的Cookie再发送给服务端。 在创建好CookieContainer实例之后,每次使用HttpWebRequest对象时, 只要将CookieContainer实例赋值给HttpWebRequest对象的CookieContainer属性,即可实现在多次的HTTP调用中Cookie的接收与发送, 最终可以模拟浏览器的Cookie处理行为,服务端也能正确识别客户的身份。

ASP.NET Form身份验证方式详解的更多相关文章

  1. ASP.net的身份验证方式有哪些?

    [转] ASP.net的身份验证方式有哪些?分别是什么原理? Asp.net的身份验证有有三种,分别是"Windows | Forms | Passport",其中又以Forms验 ...

  2. 两系统用asp.net forms 身份验证方式实现跨域登录信息共享

    1.两个系统的 web.config 都配置为 forms 验证方式( system.web —> authentication 节点) 2.在两个系统的Web.config里配置相同的 sys ...

  3. asp.net 身份验证-Form 身份验证

    一. .net身份验证简介 1.身份验证就是检测用户是否登录及所访问的资源是否有权限.当我们在访问一个受保护网络资源时,往往需要输入用户名.密码信息,或通过其他证书.第三方身份验证等方式.验证(Aut ...

  4. asp.net中常用的几种身份验证方式

    转载:http://www.cnblogs.com/dinglang/archive/2012/06/03/2532664.html   前言 在B/S系统开发中,经常需要使用"身份验证&q ...

  5. Asp.net中web.config配置文件详解(一)

    本文摘自Asp.net中web.config配置文件详解 web.config是一个XML文件,用来储存Asp.NET Web应用程序的配置信息,包括数据库连接字符.身份安全验证等,可以出现在Asp. ...

  6. jQuery Validate验证框架详解

    转自:http://www.cnblogs.com/linjiqin/p/3431835.html jQuery校验官网地址:http://bassistance.de/jquery-plugins/ ...

  7. ASP.NET MVC 随想录——探索ASP.NET Identity 身份验证和基于角色的授权,中级篇

    在前一篇文章中,我介绍了ASP.NET Identity 基本API的运用并创建了若干用户账号.那么在本篇文章中,我将继续ASP.NET Identity 之旅,向您展示如何运用ASP.NET Ide ...

  8. ASP.NET Identity 身份验证和基于角色的授权

    ASP.NET Identity 身份验证和基于角色的授权 阅读目录 探索身份验证与授权 使用ASP.NET Identity 身份验证 使用角色进行授权 初始化数据,Seeding 数据库 小结 在 ...

  9. 【转】jQuery Validate验证框架详解

    jQuery校验官网地址:http://bassistance.de/jquery-plugins/jquery-plugin-validation 一.导入js库 <script type=& ...

随机推荐

  1. 使用docker搭建redis主从模式

    前期准备: 本地Linux版本:CentOS Linux release 7.5.1804 (Core)Docker版本:Docker version 1.13.1, build dded712/1. ...

  2. (转)SQL知识_SqlParameter数组

    原文地址:http://www.cnblogs.com/FredTang/archive/2012/03/29/2423651.html 温故而知新,做做笔记先.        SqlParamete ...

  3. java正则表达式替换空格和换行符

    public class StringUtil {        public static String getStringNoBlank(String str) {            if(s ...

  4. redis中key过期事件

    刚到新公司一个月左右,有个新需求,想做定时任务,比如在用户注册时间的3天后推送用户一条消息. 从刚开始脑子里面闪现的数据库轮询,立马否定掉(浪费资源),再到linux系统的定时任务,但是当用户量过大时 ...

  5. Spring之jdbcTemplate:查询的三种方式(单个值、单个对象、对象集合)

    JdbcTemplateDemo2.java package helloworld.jdbcTemplate; import org.springframework.jdbc.core.JdbcTem ...

  6. python r(不进行转义)的用法

      第一种用法,直接针对字符串:r‘E:\ui\bbq.txt’ 第二种用法,针对变量名:r'' + 变量名

  7. asp.net 基础内容

    1. ViewData ViewBag  TempData 区别? 1.ViewData和TempData是字典类型,赋值方式用字典方式,ViewData["myName"] 2. ...

  8. wordpress写文章添加gif图片变成静态图片的解决办法

    添加文章时gif只能静态,记得在添加时选择完整尺寸,不要压缩即可

  9. Oracle表中添加外键约束

    添加主键约束: ALTER TABLE GA_AIRLINE ADD CONSTRAINT PK_AIRLINE_ID PRIMARY KEY(AIRLINE_ID); 有三种形式的外键约束: 1.普 ...

  10. 下雨天,适合学「Spring Boot」

      北方的闷热,让不少小伙伴盼着下雨,前几天北京下了场大雨,杭州也紧跟这下了场雨,就在昨天原本还很闷热的天,突然就飘泼大雨了.今天也断断续续的下着小雨,一觉醒来已经是10点了.有句话说:懒惰是人的天性 ...