在MVC3 AuthorizationAttribute中使用IPrincipal并避免使用RoleProvider

时间:2011-12-19 16:10:39

标签: c# asp.net-mvc asp.net-mvc-3

我有一个WCF服务,它返回一个实现IPrinciple和I​​Identity的对象。

我认为无论如何我都可以将其连接到MVC3授权系统而无需创建RoleProvider

e.g。所以我可以在我的AccountController登录方法中做这样的事情:

// AthenticatedUser implments both IPrinciple and IIdentity

AthenticatedUser user = wcfService.Logon(password, userName);
FormsAuthentication.SetAuthCookie(userName, false);

// Set IPrinciple so I can use IsInRole method elsewhere (or AuthorizationAttribute can reuse it)
this.HttpContext.User = authenticationClient.AuthenticatedUser;

然后在我使用时使用

[Authorize (Roles = "foo", "bar")]

我的AuthenticatedUser的IsInRole方法被调用。

然而,在我的测试/调试中,我发现this.HttpContext.User似乎没有跨请求进行维护。

编辑抱歉:我应该已经明确表示我不想在每个请求上调用我的WCF服务,我想以某种方式缓存/存储用户和角色,并且能够使用AuthorizeAttribute与IPrinciple来自我的服务。

有人可以帮忙吗?提前谢谢!

4 个答案:

答案 0 :(得分:3)

如果您使用IIS 7在本地计算机上运行此命令,请将其添加到system.webServer下的web.config:

<modules runAllManagedModulesForAllRequests="true">
    <remove name="FormsAuthentication" />
      <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />
      <remove name="UrlAuthorization" />
      <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />
      <remove name="DefaultAuthentication" />
      <add name="DefaultAuthentication" type="System.Web.Security.DefaultAuthenticationModule" />
      <remove name="RoleManager" />
      <add name="RoleManager" type="System.Web.Security.RoleManagerModule" />
</modules>

答案 1 :(得分:2)

你说“this.HttpContext.User似乎没有跨请求维护” - 这是正确的行为。每个请求都有一个唯一的HttpContext

您可能想要尝试的是:

FormsAuthentication.SetAuthCookie(userName, true);

,它会在浏览器会话之间创建一个持久的cookie。

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

答案 2 :(得分:1)

如您所知,HttpContext不会在请求中保留,因此IPrincipalIIdentity都不会 - HttpContext在每个请求开始时构建通过框架,IPrincipalIIdentity是从身份验证cookie反序列化的身份验证票证构建的。

您所描述的内容在某些方面与 WCF authentication service 听起来相似。在这种情况下,服务将对用户进行身份验证,并在对调用应用程序的响应中发回一个身份验证cookie,然后该应用程序将使用cookie在每个后续请求中构造IPrincipalIIdentity

您可以在引发IPrincipal事件时覆盖IIdentityApplication.PostAuthenticateRequest

protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
    if (Context.User != null)
    {
        var identity = Context.User.Identity;

        // define our own IIdentity and IPrincipal for an authenticated user
        if (identity.IsAuthenticated)
        {
            HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
            var ticket = FormsAuthentication.Decrypt(authCookie.Value);

            // get the roles from somewhere
            var roles = GetRoles(); 

            identity = new CustomIdentity(ticket.Name);
            IPrincipal principal = new CustomPrincipal(identity, roles);
            Context.User = Thread.CurrentPrincipal = principal;
        }
    }
}

您可以看到需要从某个位置检索角色。有了RoleProvider,这些角色可以缓存在另一个cookie中。 如果你知道你在做什么的安全性,你可以考虑复制角色在cookie中的序列化和加密方式。

另一种选择可能是将角色存储在会话中,并且可能适用于少量角色。请注意,在PostAcquireRequestState事件被提出之前,会话不可用,有些 7 events later in the application request lifecycle 而不是PostAuthenticateRequest

答案 3 :(得分:0)

可以在AuthenticateRequest事件中对您的全局asax做出反应,并像这样更改IIdentity:

public class MvcApplication : System.Web.HttpApplication
{
    public override void Init()
    {
        base.Init();
        this.AuthenticateRequest += new EventHandler(MvcApplication_AuthenticateRequest);
    }

    void MvcApplication_AuthenticateRequest(object sender, EventArgs e)
    {
        if (HttpContext.Current.Request.IsAuthenticated)
        {
            var name = HttpContext.Current.User.Identity.Name;
            var key = "User-" + name;
            var principal = HttpContext.Current.Cache["User-" + name];
            if (principal == null)
            {
                principal = GetYourUserAsIPrincipal();
                // Add to cache for 1 hour with sliding expiration
                HttpContext.Current.Cache.Insert(key, principal, null, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(1, 0, 0));
            }
            HttpContext.Current.User = principal ;
        }
    }        
}

编辑:对于缓存,您可以使用默认的ASP.NET缓存。我编辑了上面的示例。注意,ASP.NET缓存是线程安全的(http://msdn.microsoft.com/en-us/library/system.web.caching.cache.aspx)

如果将所有缓存访问逻辑包装到单个类中,那将是最好的。

与任何缓存机制一样:如果更改用户角色,则仅在缓存过期时才会反映出来。