【问题标题】:Simple Authorization in MVC3 with Forms Authentication带有表单身份验证的 MVC3 中的简单授权
【发布时间】:2012-06-08 12:42:29
【问题描述】:

我正在尝试做在 MVC3 中应该很简单的事情。

我有一个应用程序,它使用表单身份验证通过第 3 方 SSO 对用户进行身份验证。 SSO 成功登录后,会回发到我的应用程序上的特定控制器操作。然后我打电话给FormsAuthentication.SetAuthCookie(user,false);

我正在尝试实施某种级别的授权。简单地说,一个用户可以存在多个不同的角色,例如AdminDeveloper。某些控制器操作应该只对某些角色可用。用户所属角色的详细信息是通过调用另一个外部 API 获得的,该 API 返回一个简单的 JSON 响应指示。

理论上,在我设置 FormsAuthentication cookie 后,这应该像这样简单:

string[] rolelist = GetRoleListForUserFromAPI(User.Identity.Name);
HttpContext.User = new GenericPrincipal(User.Identity, rolelist);

但是,我不能在调用SetAuthCookie 之后直接调用它,因为此时HttpContext.User 没有任何意义。

我可以尝试对每个请求进行设置,但对我的应用程序的任何请求都意味着往返 API 调用。

到目前为止,我见过的最有前途的方法是创建一个自定义 Authorization 属性并覆盖 OnAuthorization 以执行以下操作:

public override void OnAuthorization(AuthorizationContext filterContext)
{
    if (<some way of checking if roles have already been set for this user, or role cache has timed out>)
    {
        string[] rolelist = GetRoleListForUserFromAPI(filterContext.HttpContext.User.Identity.Name);
        filterContext.HttpContext.User = new GenericPrincipal(filterContext.HttpContext.User.Identity,rolelist);
    }
}

然后我可以在控制器操作前使用[MyCustomAuthorization(Roles="Admin")] 来实现魔法。

但是,我不知道如何检测当前的 HttpContext.User 对象是否设置了角色,或者它是否是在某个时间之前设置的并且需要另一个 API 行程。

最好的方法是什么?

【问题讨论】:

    标签: asp.net-mvc-3 forms-authentication authorization


    【解决方案1】:

    另一种方法是将角色存储在 FormsAuthentcationTicket 的 UserData 属性中。这可以用逗号分隔的字符串来完成。

    http://msdn.microsoft.com/en-us/library/system.web.security.formsauthenticationticket.formsauthenticationticket

    然后在 AuthenticateRequest 方法上,您可以拉回票证,获取角色数据并使用通用主体将其分配给当前用户。

    【讨论】:

    • 这是个好主意 - 我可以存储一个缓存超时值,以防我想在用户会话期间多次刷新角色列表(例如,每 5 分钟)。
    【解决方案2】:

    你应该覆盖PostAuthenticateRequest

    protected void Application_OnPostAuthenticateRequest(object sender, EventArgs e) 
    {
        if (HttpContext.Current.User.Identity.IsAuthenticated)
        {
            string[] rolelist = GetRoleListForUserFromAPI(User.Identity.Name);
            HttpContext.User = new GenericPrincipal(User.Identity, rolelist);
        }
    }
    

    在表单身份验证完成处理后调用。

    http://msdn.microsoft.com/en-us/library/ff647070.aspx

    更新

    我的方法签名有误(刚刚签入了我自己的一个应用程序)。

    【讨论】:

    • Brb,去做一些测试:)
    • @jgauffin - 这看起来很有趣,但我想知道这些信息如何在后续请求中保持不变。
    • 这里好像是持久化的,把IsAdmin: @User.Identity.IsInRole("Admin")放在Master.cshtml上就是在每一页上写True
    • @ek_ny:永远不会存储身份验证。它总是为每个请求加载。如果你愿意,你可以在 PostAuthenticate 中使用会话。
    • 是的,我的问题是每个请求都会调用它,包括对 javascript 文件和图像等内容的请求。
    【解决方案3】:

    我的第一个想法是您应该研究实现自定义角色提供程序。这可能有点矫枉过正,但似乎适合基于角色的管道。

    更多信息来自 MSDN here

    【讨论】:

    • 这实际上并不是一个糟糕的方法,尽管乍一看可能会让人望而生畏。创建一个类,从 RoleProvider 继承并实现几个方法来调用 API 以获取角色信息是相当简单的。中间有一些简单的缓存,它似乎工作得很好。额外的好处是我可以拉入一个单独的 dll 并通过 nuget 分发给所有想要对此特定 API 进行授权的应用程序。
    【解决方案4】:

    令一些人震惊的是,会话对象在这里并不是一个坏主意。 如果您使用临时数据,您已经在会话中受到了打击。

    将这些数据存储在 cookie 中,好吧 - Forms auth 令牌已经在一年半前的 POET 漏洞中被利用,所以在这种情况下,有人可以简单地使用“admin”字符串形成自己的 cookie在其中使用该漏洞。

    您可以像@jgauffin 提到的那样在身份验证后执行此操作。 如果会话状态在那里不可用,您可以在 Application_PreRequestHandlerExecute 中使用它并在那里检查它。

    如果您想检查会话状态是否可用,请参阅我的代码: How can I handle forms authentication timeout exceptions in ASP.NET?

    此外,每当使用表单身份验证和会话时,您总是希望确保超时彼此同步(再次使用上面的代码)

    【讨论】:

      猜你喜欢
      • 2015-05-20
      • 1970-01-01
      • 2012-01-01
      • 1970-01-01
      • 2020-08-07
      • 2010-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多