ASP.NET会员登录重定向到未授权页面,即使用户已登录

11
我们有一个应用程序,使用ASP.net Membership提供基本的登录机制。一切正常运作,但最近我们发现,如果您在已登录状态下尝试访问登录页面,则会被重定向到“未经授权”的页面。
用户示例流程:
用户访问受保护的页面(整个应用程序都需要登录,甚至没有主页可以访问,直接重定向到登录页面)。这将重定向他们到https://www.example.com/Account/Login
用户登录后,将被重定向到主页https://www.example.com/。他们已登录,一切运作正常。
用户单击书签,该书签恰好设置为https://www.example.com/Account/Login
用户被重定向到通用的“未经授权”页面。
我在AccountController上使用了<Authorize()>属性,但在“Login”操作上使用了<AllowAnonymous()>属性,正如我们之前看到的那样,当您未登录时,它可以正常工作,但是当您登录时似乎会有一些混乱。

AccountController

<Authorize()> _
Public Class AccountController
'''other functions go here'''

<AllowAnonymous()> _
Public Function Login(ByVal returnUrl As String) As ActionResult
    ViewData("ReturnUrl") = returnUrl
    Return View()
End Function

AuthorizeRedirect 过滤器

<AttributeUsage(AttributeTargets.[Class] Or AttributeTargets.Method)> _
Public Class AuthorizeRedirect
    Inherits AuthorizeAttribute
    Private Const IS_AUTHORIZED As String = "isAuthorized"

    Public RedirectUrl As String = "~/Home/Unauthorized"

    Protected Overrides Function AuthorizeCore(httpContext As System.Web.HttpContextBase) As Boolean
        Dim isAuthorized As Boolean = MyBase.AuthorizeCore(httpContext)

        httpContext.Items.Add(IS_AUTHORIZED, isAuthorized)

        Return isAuthorized
    End Function

    Public Overrides Sub OnAuthorization(filterContext As AuthorizationContext)
        MyBase.OnAuthorization(filterContext)

        Dim isAuthorized = If(filterContext.HttpContext.Items(IS_AUTHORIZED) IsNot Nothing, Convert.ToBoolean(filterContext.HttpContext.Items(IS_AUTHORIZED)), False)

        If Not isAuthorized AndAlso filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated Then
            filterContext.RequestContext.HttpContext.Response.Redirect(RedirectUrl)
        End If
    End Sub
End Class

看到这一切,我想最简单的解决方案就是在我的登录动作中检查用户是否已经登录并自行重定向他们,像这样。

<AllowAnonymous()> _
Public Function Login(ByVal returnUrl As String) As ActionResult
    If User.Identity.IsAuthenticated() Then
        Return RedirectToAction("Index", "Home")
    End If
    ViewData("ReturnUrl") = returnUrl
    Return View()
End Function

但是AuthorizeFilter总是先出现,这是可以理解的,但我无法弄清最后缺少的部分。我只想让它在用户已登录并前往登录页面时不显示“您没有权限查看此页面”,而是将其重定向到主页。我错过了什么?


编辑以使事情更加清晰

当已经登录时,我转到/Account/Login。 这个302重定向我到/Home/Unauthorized(我的自定义页面)。 然而,我仍然登录。

网络请求

Network request to Login page, which 302 redirects to Unauthorized

未经授权的页面。请注意,突出显示的黄色部分表明我仍然登录。只有在登录时才会出现这种情况。如果未登录,则不会看到任何内容。

Unauthorized page

问题似乎是应用程序不知道当我已经登录并尝试进入带有[AllowAnonymous]属性的页面时该怎么做。如果说有什么好处的话,那么我在这里看到的行为比它再次给我一个登录页面要好,因为那会让人感到困惑,但是,仍然不理想。

编辑2-逐行浏览代码

以下是逐行浏览代码的结果。

页面/Account/Login在登录状态下。

第一个断点在AuthorizeRedirect过滤器中的OnAuthorization子程序中。

Public Overrides Sub OnAuthorization(filterContext As AuthorizationContext)
    MyBase.OnAuthorization(filterContext)

    Dim isAuthorized = If(filterContext.HttpContext.Items(IS_AUTHORIZED) IsNot Nothing, Convert.ToBoolean(filterContext.HttpContext.Items(IS_AUTHORIZED)), False)

    If Not isAuthorized AndAlso  filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated Then
        filterContext.RequestContext.HttpContext.Response.Redirect(RedirectUrl)
    End If
End Sub

Dim isAuthorized开头的行返回False。 filterContext.HttpContext.Items(IS_AUTHORIZED)不存在于项目列表中(为null)。

这意味着下一个If语句将计算为True(Not isAuthorized AndAlso ...IsAuthenticated),导致重定向到RedirectUrl

之后,它似乎会重新经历相同的步骤,但这次计算结果为false,这意味着重定向不会发生,尽管我猜这只是“未经授权”的页面加载并再次运行相同的代码。

我试图将以下块添加到AccountControllerLogin函数的顶部。

    If User.Identity.IsAuthenticated() Then
        Return RedirectToAction("Index", "Home")
    End If

当然,由于过滤器是在动作之前运行的,所以这段代码只有在它已经将我重定向到“未经授权”的状态之后才会被执行(通过逐步调试进行验证)。

AuthorizeRedirect 应用在哪里? - Bozhidar Stoyneff
现在无法访问代码,但我很确定它是在标准的FilterConfig中设置的,类似于“filters.add(Authorizeredirect)”。 - Rob Quincey
用户似乎未被授权。这是真的吗?在重定向到未经授权后,您可以访问/home/index吗? - Dave Alperovich
在被重定向到“未授权”页面后,我仍然可以访问我的任何已授权页面。此外,所有仅在登录后才出现的链接和我放在顶部角落的“以[user]身份登录”的信息都正常显示,因此它知道我仍然登录着。我将编辑帖子以使其更清晰。 - Rob Quincey
1
你尝试过逐步执行(断点)你的 AuthorizeRedirect 代码,查看在加载登录页面时值是什么吗? - Francis Lord
抱歉,我刚回到工作岗位就开始处理这个问题。我会更新帖子(再次)以展示步骤结果。如果有人提供答案,我将重新激活赏金并授予它。 - Rob Quincey
1个回答

2

在其 OnAuthorization 方法中,AuthorizationAttribute 的基类拥有以下代码:

bool skipAuthorization = filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true)
                         || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true);

if (skipAuthorization)
{
    return;
}

if (AuthorizeCore(filterContext.HttpContext))
// ...

如果控制器操作有AllowAnonymousAttribute,则您的AuthorizeCore方法将不会被调用。因此,filterContext.HttpContext.Items(IS_AUTHORIZED)将永远不会被设置。您可以直接从这里复制代码以实现OnAuthorization,而不调用基类。这样,您可以按照任何方式处理缓存。顺便说一句,我认为,如果授权失败,请求管道中的后续过程将自动重定向到登录页面。这就是为什么OnAuthorization的基本实现将filterContext.Result设置为新的HttpUnauthorizedResult实例的原因。因此,不完全清楚为什么要首先覆盖OnAuthorization并进行重定向。如果想要某种自定义授权代码,只需从AuthorizeCore返回truefalse即可。

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