设置授权和 "Access-denied" 页面

Setting up authorization and a "Access-denied" page

我正在开发 ASP.Net MVC 应用程序。我想拒绝访问所有未经身份验证或不在 AD 组中的用户。只有这个 AD 组应该有访问权限。 "You Shall Not Pass!" 页面除外。任何人都可以访问它。

在项目根目录中,我的 web.config 中有这个(为简洁起见,文件的其余部分被修剪):

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <system.web>
        <customErrors mode="On">
            <error statusCode="401" redirect="/ui/Other/YouShallNotPass.html" />
        </customErrors>
        <compilation debug="true" targetFramework="4.5.2" />
        <httpRuntime targetFramework="4.5.2" />
        <authentication mode="Windows" />
        <authorization>
            <allow roles="allowedrole"/>
            <deny users="*"/>
        </authorization>
    </system.web>
    <system.webServer>
        <httpErrors>
            <remove statusCode="401" />
            <error statusCode="401" 
                   subStatusCode="2" 
                   responseMode="ExecuteURL" 
                   path="/ui/Other/YouShallNotPass.html" />
        </httpErrors>
    </system.webServer>
</configuration>

我还有一个 web.config 坐在 ui/Other/YouShallNotPass.html 旁边。我希望这允许任何人访问此页面,无论是否经过身份验证:

<configuration>
    <system.web>
        <authorization>
            <allow users="?"/>
        </authorization>
    </system.web>
</configuration>

我可以通过将 AD 组设置为不存在的组来对此进行测试。我不属于不存在的组,所以我应该会看到 YouShallNotPass.html 页面。

它没有按预期工作。我的浏览器出现以下错误:

Error message 401.2.: Unauthorized: Logon failed due to server configuration. Verify that you have permission to view this directory or page based on the credentials you supplied and the authentication methods enabled on the Web server.

如果我直接请求 YouShallNotPass.html,我可以在提示输入 user/pass 后访问它。

我做错了什么?为什么当用户未被授权时它不提供 401 页面?

查看此 question 因为它解释了为什么您的解决方案不起作用。

因此,一旦您使用 [Authorize] 注释所有安全控制器操作,您就可以添加自定义 ExceptionFilter

像这样

public class HandleUnauthorizedAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        base.OnException(filterContext);

        if (filterContext.Exception.GetType() != typeof (SecurityException)) return;

        var controllerName = (string) filterContext.RouteData.Values["controller"];
        var actionName = (string) filterContext.RouteData.Values["action"];
        var model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);

        filterContext.Result = new ViewResult
        {
            ViewName = "Unauthorized",
            ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
            TempData = filterContext.Controller.TempData
        };
        filterContext.ExceptionHandled = true;
        filterContext.HttpContext.Response.Clear();
        filterContext.HttpContext.Response.StatusCode = 403;
        filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
    }
}

然后在这里接线

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleUnauthorizedAttribute());
    }
}

通过将以下内容添加到我的 global.asax 来设法解决此问题:

protected void Application_EndRequest(object sender, EventArgs e)
{
    if (Response.StatusCode != 401)
        return;

    Response.ClearContent();
    Response.WriteFile("~/ui/Other/YouShallNotPass.html");
    Response.ContentType = "text/html";
}

不过,我更愿意使用 web.config 来执行此操作。