缺少尾部斜杠的虚拟目录

3
我们正在开发一个ASP.NET MVC 5.1应用程序,将其部署到客户站点上,我们将无法控制他们的IIS配置。该应用程序使用ASP.NET Identity 2.0.0进行身份验证和用户管理。在内部测试期间,我们在运行Windows Server 2012 R2和IIS 8的测试服务器上有不同实例的虚拟目录(作为单独的应用程序和单独的应用程序池)。
我收到了一个错误报告,尝试使用以下URL登录:

https://server.domain.com/VirtualDirectory/Account/Login?ReturnUrl=%2fVirtualDirectory

在循环中出现结果,用户已登录但被重定向回登录页面而非应用程序根/主页。显然的突出点是虚拟目录名称缺少尾斜杠。如果提供了编码后的尾斜杠,或省略了returnUrl,或者其不是本地URL,则应用程序将正确地成功重定向。
这不是我们登录逻辑的问题,因为已经登录且在应用程序的根处的用户简单地通过删除虚拟目录名称后面的尾斜杠将被重定向回登录页面。

https://server.domain.com/VirtualDirectory

根据 IIS generates courtesy redirect when folder without trailing slash is requested,当请求没有斜杠的文件夹时,IIS会生成礼貌重定向。

当浏览器请求http://www.servername.de/SubDir这样的URL时,浏览器将被重定向到http://www.servername.de/SubDir/。URL末尾包含一个斜杠... Internet Information Server (IIS)首先将SubDir视为应该返回给浏览器的文件。如果找不到此文件,则IIS检查是否存在具有此名称的目录。如果存在具有此名称的目录,则向浏览器返回带有302“对象移动”响应消息的礼貌重定向。此消息还包含有关带有尾随斜杠的目录的新位置的信息。随后,浏览器启动对带有尾随斜杠的URL的新GET请求。

但是,我实际上从Fiddler获取以下响应-

获取 https://server.domain.com/VirtualDirectory

302 重定向到 /VirtualDirectory/Account/Login?ReturnUrl=%2fVirtualDirectory

默认路由未经过微软模板修改-

 routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );

主页控制器被装饰了一个 [Authorize] 属性,这当然要求未经身份验证的用户登录。

与身份验证相关的 Web.config 设置为-

<authentication mode="None">
   <forms cookieless="UseCookies" loginUrl="~/Account/Login" name="OurCompanyAuthentication" timeout="120" />
</authentication>
<authorization>
   <allow users="?" />
   <allow users="*" />
</authorization>

表单元素已经通过实验被证明是必要的,以正确映射到登录页面,并防止继承配置在应用程序根目录下查找login.aspx页面。该配置与Startup.Auth.cs的配置匹配,如下所示-
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    CookieHttpOnly = true, //In supported browsers prevent JavaScript from accessing the authentication cookie
    CookieName = "OurCompanyAuthentication",
    CookiePath = VirtualPathUtility.ToAbsolute("~/"),
    ExpireTimeSpan = new TimeSpan(hours: 2, minutes: 0, seconds: 0), //Cookie expires two hours after it is issued
    LoginPath = new PathString("/Account/Login"),
    SlidingExpiration = true //Cookie will be re-issued on requests more than halfway through expiration window
});

问题-

是什么导致无条件生成302重定向到登录页面,返回的returnUrl未被修改/纠正(无论用户是否已登录),如何恢复执行附加斜杠的302重定向所描述的行为。

如果可以有条件地将其定向到具有正确return URL的登录页面,那就更好了,但首要要求是避免不正确的重定向到登录页面和随后的循环。如果302重定向到应用程序根目录,然后跟随进一步重定向到登录页面(其中用户未经身份验证或其票证已过期),则可接受。

我已经研究了URL重写,但域和虚拟路径事先未知于开发人员,因为这些可能在每个客户站点上都不同 - 就像主机服务器上使用或不使用虚拟目录一样。


尝试使用 CookiePath = VirtualPathUtility.ToAbsolute("~"),注意斜杠不在那里。 - Imran Qadir Baksh - Baloch
1个回答

2

我认为这段代码应该能够实现我想要的目标,既可以修复缺少斜杠的问题,又可以在用户未经身份验证时重定向到登录页面,以避免浪费重定向。

protected void Application_ResolveRequestCache(object sender, EventArgs e)
{
    //If the application is installed in a Virtual Directory and the trailing slash is ommitted then permantently redirect to the default action
    //To avoid wasted redirect do this conditional on the authentication status of the user - redirecting to the login page for unauthenticated users
    if ((VirtualPathUtility.ToAbsolute("~/") != Request.ApplicationPath) && (Request.ApplicationPath == Request.Path))
    {
        if (HttpContext.Current.User.Identity.IsAuthenticated)
        {
            var redirectPath = VirtualPathUtility.AppendTrailingSlash(Request.Path);

            Response.RedirectPermanent(redirectPath);
        }

        var loginPagePath = VirtualPathUtility.ToAbsolute("~/Account/Login");

        Response.StatusCode = 401;
        Response.Redirect(loginPagePath);
    }
}

我将cookie路径设置为应用程序目录,但是当请求缺少尾部斜杠时,用户cookie未被发送,因此用户无法通过身份验证。因此,我已经将其移动到请求生命周期的早期事件,并简化为-

protected void Application_BeginRequest(object sender, EventArgs e)
{
    //If the application is installed in a Virtual Directory and the trailing slash is ommitted then permantently redirect to the default action
    //To avoid wasted redirect do this conditional on the authentication status of the user - redirecting to the login page for unauthenticated users
    if ((VirtualPathUtility.ToAbsolute("~/") != Request.ApplicationPath) && (Request.ApplicationPath == Request.Path))
    {
        var redirectPath = VirtualPathUtility.AppendTrailingSlash(Request.Path);

        Response.RedirectPermanent(redirectPath);

        return;
    }
}

顺便提一下,该事件在应用程序中与应用程序头相关的代码更多 - 以防明显冗余的返回语句引起任何人的担忧。在应用程序的上下文中,它不是冗余的。 - pwdst

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接