使用表单身份验证实现MVC3中的简单授权

4

我正在尝试在MVC3中执行一项简单的任务。

我的应用程序使用表单身份验证来验证与第三方SSO的用户身份。成功登录后,SSO会将用户回传到我的应用程序上的特定控制器操作。然后我调用FormsAuthentication.SetAuthCookie(user,false);

我正在尝试实现某种程度的授权。简而言之,用户可以存在于多个不同的角色中,例如管理员开发人员。某些控制器操作只应该对某些角色可用。通过调用另一个外部API获取用户所属角色的详细信息,该API返回简单的JSON响应。

理论上,只需在设置FormsAuthentication cookie后执行以下操作即可:

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

然而,在调用SetAuthCookie之后,我不能直接调用它,因为此时的HttpContext.User没有任何有意义的内容。

我可以尝试在每个请求上设置这个值,但是这意味着我的应用程序每次请求都需要进行一次 API 调用。

到目前为止,我看到的最有希望的方法是创建一个自定义的授权属性,并覆盖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 请求来完成。
对于这个问题,最好的方法是什么?
4个回答

2

这是一个好主意 - 我可以合理地存储缓存超时值,以防在用户会话期间我想要多次刷新角色列表(比如每五分钟)。 - growse

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

更新

我之前的方法签名是错误的(刚刚检查了我的一个应用程序)。


马上回来,去测试一下这个 :) - growse
@jgauffin - 这看起来很有趣,但我想知道这些信息如何在后续请求中保留。 - ek_ny
@ek_ny:身份验证从不被存储。它总是在每个请求中加载。如果您愿意,可以在PostAuthenticate中使用会话。 - jgauffin
是的,我对此有问题的原因是它在每个请求中都被调用,包括对javascript文件和图像的请求。 - growse
没有绕过这个问题的方法。这是放置身份验证的适当位置,因为必须在任何授权之前完成。它可能看起来像是浪费资源,但并不是很重。当然,您可以使用会话来缓存角色。 - jgauffin
显示剩余3条评论

1

我的第一反应是您应该考虑实现自定义角色提供程序。这可能有些过度,但似乎符合基于角色的管道。

更多信息请参见MSDN 此处


这并不是一个坏方法,虽然一开始可能会让人畏惧。创建一个类、从RoleProvider继承并实现一些方法来调用API获取角色信息相当简单。在中间加上一些简单的缓存,它看起来运行得非常好。额外的好处是我可以将其打包成一个独立的dll文件通过NuGet分发给所有需要授权对接此特定API的应用程序。 - growse

1

对一些人来说,使用会话对象并不是个坏主意。

如果您使用临时数据,您已经为会话付出代价了。

将此数据存储在cookie中,那么 - 表单授权令牌已经在 POET 漏洞中被利用了一年半,因此在那种情况下,某人可以简单地使用该漏洞创建自己的带有 "admin" 字符串的 cookie。

您可以像 @jgauffin 提到的那样在 post authenticate 中执行此操作。 如果那里没有可用的会话状态,则可以在 Application_PreRequestHandlerExecute 中使用它并在那里进行检查。

如果要检查会话状态是否可用,请参阅以下代码: 如何在ASP.NET中处理表单身份验证超时异常?

此外,无论何时使用表单授权和会话,您都需要确保它们之间的超时时间同步(再次参见上面的代码)


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