配置IIS Windows身份验证以返回HTTP 403而不是HTTP 401,以获得经过身份验证的用户拒绝的权限

Ste*_*sen 5 asp.net iis

一位同事今天问我如何配置IIS 7.5以使用集成的Windows身份验证模拟简单的Intranet网站,其中只有静态内容仅限于Active Directory中的特定组(例如"管理员").

事实证明,当经过身份验证的用户没有对请求资源的权限时,IIS会发送HTTP 401响应.拒绝权限可能是NTFS文件ACL或system.webServer/security/authorizationIIS配置中定义的ACL 的结果.

所有主流浏览器似乎都将此401解释为最终用户提供了无效的Windows用户名/密码凭据,从而提示用户输入其用户名/密码.在显示401响应主体/内容之前,IE似乎提示最多3次.Chrome和Safari似乎反复提示用户.

这可能会让最终用户感到困惑,他们不断重复输入有效的Windows用户名/密码,只是再次提示.

更好的方法是IIS返回HTTP 403而不是HTTP 401:

403禁止

服务器理解请求,但拒绝履行请求.授权不会帮助和请求不应重复.

资料来源:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4

如何配置IIS集成Windows身份验证以发送HTTP 401以进行登录失败,如何将HTTP 403发送以拒绝权限?

Ste*_*sen 9

什么没有奏效

我尝试了几乎所有IIS配置排列,没有运气.我使用Windows身份验证提供程序设置,例如NTLM,Negotiate和Negotiate:Kerberos.他们似乎都没有做到这一点.这并不是一个惊喜,因为浏览器决定再次尝试身份验证,即使他们可能不应该.

401未经授权

该请求需要用户身份验证.响应必须包含WWW-Authenticate头字段(第14.47节),其中包含适用于所请求资源的质询.客户端可以使用合适的Authorization头字段重复请求(第14.8节).如果请求已包含授权凭据,则401响应表示授权已拒绝这些凭据.

资料来源:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2

做了什么工作

我决定利用一些down和dirty ASP.NET来解决这个问题.下面借助服务器上的几个文本文件和ASP.NET动态编译实现响应重写.(我之前从未使用过动态编译,但是,对于静态站点来说,编译似乎有点过分.)

下面的Global.asax文件挂钩EndRequest事件,如果用户成功通过身份验证为Windows用户,则将HTTP 401响应重写为HTTP 403,但由于某些其他原因,请求被拒绝(我认为原因必须是授权失败).

web.config文件包含通过ASP.NET管道路由所有请求的条目,并拒绝访问任何不是"Sales"Windows组成员的经过身份验证的用户.

此解决方案假定您有一个在IIS集成管道模式(即非经典模式)下运行的应用程序,您启用了Windows身份验证,并禁用了所有其他身份验证方案.

/Global.asax:

<Script language="C#" runat="server">
     void Application_EndRequest() {
          // rewrite HTTP 401s to HTTP 403s if the user is authenticated using 
            // integrated Windows auth with impersonation, but, 
            // the user lacks permissions to the requested URL
            if (Context.User != null && 
                    Context.User.Identity != null &&
                        Context.User.Identity.IsAuthenticated &&
                            Context.User is System.Security.Principal.WindowsPrincipal && 
                                Context.Response.StatusCode == 401) 
            {
                Context.Response.Clear();
                Context.Response.StatusCode = 403;
            }
        }
</script>
Run Code Online (Sandbox Code Playgroud)

/web.config:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <security>
            <authorization>
                <remove users="*" roles="" verbs="" />
                <add accessType="Allow" roles="Sales" />
            </authorization>
        </security>
      <modules runAllManagedModulesForAllRequests="true" />
    </system.webServer>
</configuration>
Run Code Online (Sandbox Code Playgroud)

为了将来的参考,我创建了一个Gist @ https://gist.github.com/steve-jansen/6234700