ASP.NET成员身份登录在用户登录时重定向到未授权

Rob*_*cey 10 vb.net asp.net asp.net-membership membership-provider asp.net-mvc-5

我们有一个使用ASP.net Membership的应用程序来提供基本的登录机制.一切正常,但最近我们发现,如果您在登录时尝试进入登录页面,则会被重定向到"未授权"页面.

示例用户流程.

用户进入安全页面(整个应用程序需要登录,甚至没有您可以访问的主页,只需重定向直接登录).这会将它们重定向到https://www.example.com/Account/Login.

用户登录并重定向到主页https://www.example.com/.他们已登录,一切正常.

用户单击恰好设置为https://www.example.com/Account/Login的书签

用户被重定向到通用的Unauthorized页面.

<Authorize()>在我的AccountController上有属性,但是<AllowAnonymous()>'Login'操作的属性,正如我们之前看到的,当你没有登录时工作正常,但是当你在它时似乎有点混乱.

的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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

看到这一切,我认为最简单的解决方案是检查用户是否已登录我的登录操作并将其自行重定向,如下所示.

<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
Run Code Online (Sandbox Code Playgroud)

但是AuthorizeFilter总是首先跳进去,这是可以理解的,但我无法弄清楚最后一个缺失的部分.我想要的是,如果用户在登录时进入登录屏幕,则不显示"您无权查看此页面",而是将其重定向到主页.我错过了什么?


编辑以使事情更清楚

当我已经登录时,我会去/Account/Login.这302会将我重定向到/Home/Unauthorized(我的自定义页面).但是,我仍然登录.

网络请求

网络请求登录页面,302重定向到Unauthorized

未经授权的页面.请注意突出显示的黄色部分显示我仍然登录.只有在您登录时才会出现.如果您没有登录,则不会获得任何信息.

未经授权的页面

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


编辑2 - 逐行逐步执行代码

以下是逐行逐步执行代码的结果.

/Account/Login登录时的页面.

首先断点OnAuthorizationAuthorizeRedirect过滤器.

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
Run Code Online (Sandbox Code Playgroud)

以" Dim isAuthorizedFalse" 开头的行.filterContext.HttpContext.Items(IS_AUTHORIZED)什么都不是(在项目列表中不存在).

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

发生这种情况之后,似乎会回到相同的步骤,除非这次评估为false,这意味着重定向不会发生,尽管我猜这只是"未授权"页面加载并再次运行相同的代码.

我试图将以下块添加到Login函数的顶部AccountController.

    If User.Identity.IsAuthenticated() Then
        Return RedirectToAction("Index", "Home")
    End If
Run Code Online (Sandbox Code Playgroud)

但是,当然,由于过滤器在操作发生之前运行,因此直到它已经重定向到(通过单步执行验证)之后才会触发该代码Unauthorized.

Mar*_*own 2

AuthorizationAttribute的基类在其方法中包含以下代码OnAuthorization

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

if (skipAuthorization)
{
    return;
}

if (AuthorizeCore(filterContext.HttpContext))
// ...
Run Code Online (Sandbox Code Playgroud)

因此,如果控制器操作已AllowAnonymousAttribute定义,则您的AuthorizeCore方法将不会被调用。

因为那filterContext.HttpContext.Items(IS_AUTHORIZED)永远不会被设定。

您只需从此处复制代码即可实现OnAuthorization,而无需调用基类。这样您就可以按照您想要的方式处理缓存。

顺便说一句,我的印象是,如果授权失败,请求管道中的后续进程无论如何都会重定向到登录页面。这就是为什么将 的基本实现OnAuthorization设置filterContext.Result为新HttpUnauthorizedResult实例的原因。因此,尚不完全清楚为什么您要覆盖OnAuthorization并首先进行重定向。如果您想要某种自定义授权代码,只需返回truefalse来自AuthorizeCore应该就足够了。