缺少尾部斜杠的虚拟目录

时间:2014-02-26 13:02:14

标签: asp.net-mvc iis asp.net-identity virtual-directory asp.net-mvc-5.1

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

我报告了一个错误,尝试使用像

这样的网址登录
  

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 -

  

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

我实际上虽然得到了Fiddler的以下回复 -

  

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

     

302重定向到/ VirtualDirectory /帐户/登录?ReturnUrl =%2fVirtualDirectory

默认路由未经Microsoft模板修改 -

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

家庭控制器装饰有[授权]属性,当然需要未经身份验证的用户登录。

与身份验证相关的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重定向并附加斜杠。

如果我可以有条件地使用正确的返回URL指向登录页面那么好,但主要要求是避免错误地重定向到登录页面和后续循环。如果302重定向到应用程序根目录,然后进一步重定向到登录页面(用户未经过身份验证或其票证已过期),那么这是可以接受的。

我已经研究了URL重写,但开发人员并不知道域和虚拟路径,因为每个客户站点上的域和虚拟路径可能不同 - 主机服务器上的虚拟目录的使用或其他方式也是如此。

1 个答案:

答案 0 :(得分: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;
    }
}