根据http协议对状态码的定义,401代表未经授权的访问。在IIS中在401的基础上定义了一系列的子状态码来代表各种不同情况下未经授权访问的具体原因。
401.1 - Logon failed. -登陆失败
401.2 - Logon failed due to server configuration.-基于服务器配置的登陆失败
401.3 - Unauthorized due to ACL on resource.-资源访问控制列表返回未授权
401.4 - Authorization failed by filter.-服务器filter返回未授权
401.5 - Authorization failed by ISAPI/CGI application.-服务器ISAPI/CGI返回未授权
这篇文章中主要来介绍各种常见401错误的重现方法相应的解决方案。在这之前首先介绍一下对于401问题的定位方式,即如何知道发生了401错误。
401问题的定位方式
对于401问题的定位我们要区分我们是在客户端还是在服务器端访问页面,因为服务器对于错误返回消息的配置不同使我们在浏览器中看到的结果大相径庭。
客户端对于401问题的定位
在客户端我们遇到401问题时经常会见到的页面如下,这是一个IIS 7及以上版本的默认401错误页面。
服务器也可以配置使用不同的错误页面来呈现这个问题。所以更为精确的定位方式我们还是要参考服务器返回的状态码。
在IE中打开F12开发者工具,切换到network选项卡,然后点start capturing。访问该网页,抓取的network追踪记录中就可以看到401的返回码。
服务端对于401问题的定位
在服务器端访问出错页面的结果如下,一般默认配置情况下服务器会显示详细的错误信息。
另外我们也可以通过查看IIS日志来确定详细的出错码。
打开IIS - Loggingfeature,注意需要确定SelectFields中选中了Protocal Status和Protocal Substatus (默认情况下是选中的)。
然后到日志记录中查找相应的请求记录,从而确定返回状态码与子码分别是什么。
#Software:Microsoft Internet Information Services 8.0
#Version:1.0
#Date:2012-10-05 04:34:24
#Fields:date time s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ipcs(User-Agent) cs(Referer) sc-status sc-substatus sc-win32-status time-taken
2012-10-0504:34:24 ::1 GET /iisstart.htm - 80 - ::1Mozilla/5.0+(compatible;+MSIE+10.0;+Windows+NT+6.2;+WOW64;+Trident/6.0) -401 2 5 753
401.1 - Logon Failed
问题重现
401.1问题一般是因为用户名和密码无法在服务器通过认证导致。要重现这个问题可以在服务器禁用匿名访问方式。启用basic认证方式。
再次访问页面,就会弹出如下需要输入用户名和密码的对话框。
如果三次输入的用户名和密码出错的话,你就会得到这样一个页面
如果是服务器端详细的信息是这样的
解决方案
这种问题一般发生的原因就是用户名和密码不正确。可以尝试重置用户密码或通过另外一个用户登陆来定位是否是用户名密码的问题。
另外还有一种较为常见的在服务器的应用程序池运行在一个本地或者域账户的情况下,因为kerberos认证失败而导致401.1错误。这种问题的解决办法可以参照这里。
401.2 - Logon failed due to server configuration
问题重现
401.2问题一般是因为服务器配置原因导致的,比如最常见的服务器启用了基于401Challenge的认证方式,那么客户端尝试发送匿名请求给服务器,服务器就会返回401 Challenge给客户端,客户端受到这个回复之后就会弹出认证对话框,如上面重现401.1问题时的认证对话框。而这个401Challenge请求在服务器日志里面会记录成401.2。
所以当在日志中发现下面这种401.2错误的时候不用担心,因为可以看到用户名为空的请求返回了401.2,之后又有带了用户名的请求返回了200(请求成功)。
2012-10-0507:26:40 ::1 GET /iisstart.htm - 80 - ::1Mozilla/5.0+(compatible;+MSIE+10.0;+Windows+NT+6.2;+WOW64;+Trident/6.0) -401 2 5 7
2012-10-0507:27:08 ::1 GET /iisstart.htm - 80 - ::1Mozilla/5.0+(compatible;+MSIE+10.0;+Windows+NT+6.2;+WOW64;+Trident/6.0) -401 2 5 8
2012-10-0507:27:26 ::1 GET /iisstart.htm - 80domain\user1 ::1Mozilla/5.0+(compatible;+MSIE+10.0;+Windows+NT+6.2;+WOW64;+Trident/6.0) - 200 0 0 409
另外一种401.2的重现方式非常简单,是把所有的认证方式全部禁用,再次访问网页就会返回401.2错误消息。在第一节问题一般定位方式截图中显示的401.2错误就是把所有的认证方式禁用得到的。
第三种方式比较特殊,我们可以通过服务器端配置authorization rule来禁止或者允许特定的用户访问。为了重现问题我们禁用所有的用户。
访问网页之后发现返回401.2错误,在服务器详细错误中在这里我们要关注的一点是Module中显示的信息,这里我们可以看到是UrlAuthorizationModule,而不是前面禁用了所有认证方式的IISWeb Core,这就告诉我们出错的地方在于配置的的Authorization Rules。
解决方案
第一种情况属于正常现象,不是问题
第二种情况即没有启用任何认证方式,只需要在IIS Manager里面启用需要的认证方式即可。
第三种情况即被Authorization Rule阻挡,则需要分析这个authorization rule是否是否必须来决定对他的更改。
还有第四种情况,就是在配置文件中指定了认证方式但是相应的认证模块没有安装。这种情况多发生在系统迁移的过程中。解决方案就是把相应的认证模块安装上。
401.3 - Unauthorized due to ACL on resource
问题重现
401.3问题是最明显的一个,即没有权限访问文件(夹),要重现这个问题我们可以将要访问的文件的安全性设置为拒绝任何人访问。然后请求该文件就会得到以下出错信息。
在这里需要关注的错误显示的Logon User,这样可以知道你正在以什么身份访问文件。
解决方案
调试401.3需要知道两件事,
第一是什么资源组织了访问,问题出在上级文件夹还是文件本身。
第二是我们以什么样的身份访问资源的时候被阻止了。
调试这个问题最好的工具是Process Monitor。打开Process Monitor过滤w3wp进程对文件的访问。就可以看到access deny被记录下来。同时还有访问身份信息。这样我们就可以在文件系统做相应的更改来解决问题。
401.4 - Authorization failed by filter
问题重现
401.4问题重现比起前面要更加费些周折,因为这个错误的意思是自定义filter返回了拒绝访问的错误。所以我们要先创建一个自定义filter。
这里值得注意的是在IIS6上只能创建自定义ISAPI Filter来处理进入的各种请求,创建ISAPI Extension来处理特定的请求。而这两种扩展方式在IIS7及以上的版本不再被推荐使用,取而代之的是创建Http Module和HttpHandler来扩展IIS的功能,而且支持托管和非托管两种模式,开发和调试都更加方便。
为了更形象的重现这个问题,我们还是采用ISAPI Filter的方式。创建ISAPI Filter需要两个文件。
401-4-Filter.def
LIBRARY 401-4-Filter ;DESCRIPTION 'filter to return 401.4' EXPORTS HttpFilterProc GetFilterVersion401-4-Filter.C
#include <windows.h> #include <httpfilt.h> #define DEFAULT_BUFFER_SIZE 1024 #define MAX_BUFFER_SIZE 4096 DWORD OnPreprocHeaders( IN HTTP_FILTER_CONTEXT * pfc, IN HTTP_FILTER_PREPROC_HEADERS * pPPH ); BOOL WINAPI GetFilterVersion( HTTP_FILTER_VERSION * pVer ) { pVer->dwFilterVersion = HTTP_FILTER_REVISION; lstrcpyn( pVer->lpszFilterDesc, "Filter to return 401.4 error", SF_MAX_FILTER_DESC_LEN ); pVer->dwFlags = SF_NOTIFY_ORDER_HIGH | SF_NOTIFY_PREPROC_HEADERS ; return TRUE; } DWORD WINAPI HttpFilterProc( IN HTTP_FILTER_CONTEXT * pfc, DWORD dwNotificationType, LPVOID pvNotification ) { switch ( dwNotificationType ) { case SF_NOTIFY_PREPROC_HEADERS: return OnPreprocHeaders( pfc, (HTTP_FILTER_PREPROC_HEADERS *) pvNotification ); } return SF_STATUS_REQ_NEXT_NOTIFICATION; } DWORD OnPreprocHeaders( HTTP_FILTER_CONTEXT * pfc, HTTP_FILTER_PREPROC_HEADERS * pPPH ) { SetLastError( ERROR_ACCESS_DENIED ); return SF_STATUS_REQ_ERROR; }
将其编译成与application pool CPU架构(x86|x64)相同的classlibrary。然后通过IIS Manager功能视图中ISAPI Filter中加入编译产生的Dll。之后请求页面就会得到如下返回。
解决方案
可以看到详细信息中并没有显示出出问题的模块,这里就需要我们通过一定的调试手段来定位这个出错的模块在哪里。比较方便的方式是通过failed
request tracing追踪请求,找到出错的模块。
详细的方法可以参考这里
http://support.microsoft.com/kb/942079/zh-cn
401.5 - Authorization failed by ISAPI/CGI application
问题重现
401.5问题的出现原因可能源于以下几个方面阻止了请求。
- 自定义的ISAPIextension
- CGI程序
- ASP页面
- 自定义handler
这里通过一个managedhttp handler来重现这个问题,将以下代码放在网站根目录下App_Code文件夹,然后在IISManager功能视图里选在Handler Mapping,添加Managed Handler,选择一个对应的请求路径,我们这里用它来处理所有*.aa的文件请求,之后在网站里面加入一个test.aa文件,通过浏览器发送请求。
using System.Web; namespace 4015Handler { public class 4015Handler: IHttpHandler { public bool IsReusable { get { return true; } } public void ProcessRequest(HttpContext context) { context.Response.StatusCode = 401; context.Response.SubStatusCode = 5; context.Response.End(); } } }
就会得到这样的结果。
解决方案
对于这个问题的定位方式与401.4一样,可以通过failedrequest tracing来跟踪消息,找到出错的模块。
详细方法如下
http://support.microsoft.com/kb/942078/zh-cn