在ASP.NET MVC中使用表单验证(Forms Authentication)实现自定义IPrincipal

13
这应该很简单,但是在我查阅了所有资料之后,我还是没弄明白。这是我的需求:我有一张自定义的用户表(目前没有角色),我想对其进行授权。为此,我需要实现一个自定义的Membership提供程序,我已经完成了这个步骤。但我的问题是,如何设置一个自定义的IPrincipal到HttpContext.Current.User中?这应该在哪里发生?假设我的设置如下所示:
public class CustomMembershipProvider : MembershipProvider
{
   private IUserRepository _repository;

   public CustomMembershipProvider(IUserRepository repository)
   {
      _repository = repository;
   }

   public override bool ValidateUser(string username, string password)
   {
      //check user repository 
   }

   //additional methods omitted for brevity 
}

我有一个自定义用户类,实现了IPrincipalIIdentity接口。

 public class CustomUser : IPrincipal
    {
        public CustomUser(string name, int userId, string additionalInfo)
        {
            Identity = new CustomIdentity(name, userId, additionalInfo);
        }

        public IIdentity Identity { get; private set; }

        public bool IsInRole(string role)
        {
            return true;
        }
    }

    public class CustomIdentity : IIdentity
    {
        public CustomIdentity(string name, int userId, string additionalInfo)
        {
            Name = name;
            UserId = userId;
            AdditionalInfo = additionalInfo;
        }

        public string Name { get; private set; }

        public string AuthenticationType
        {
            get { return "CustomAuth"; }
        }

        public bool IsAuthenticated
        {
            get { return !String.IsNullOrEmpty(Name); }
        }

        public int UserId { get; private set; }
        public string AdditionalInfo { get; private set; }
    }
所以我的问题是,将Context.User设置为此自定义用户实例的正确位置在哪里?我需要一个自定义授权属性吗?如果需要,那么它应该是什么样子?

你最终处理这个问题的进展如何?我自己正在做类似的事情,目前还处于早期阶段。 - ETFairfax
我和你一样,我快要准备好创建一个扩展方法来完成这项工作了! - Jason Foglia
1个回答

2
我建议为所有控制器使用自定义控制器基类。然后,在OnAuthorization中调用:
protected override void OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext)
{
    // Actual Authentication
    HttpContext.User = user; 
    Thread.CurrentPrincipal = user;
    base.OnAuthorization(filterContext);
}

我不确定如何调用成员资格提供程序,因为我正在手动完成操作,但我认为你可以通过静态访问Membership.Provider来执行实际的对象构造。

我需要一个自定义的授权属性吗?

不需要。请注意验证和授权之间的区别:验证在您的系统中建立用户的身份。授权允许或拒绝特定请求。因此,AuthorizeAttribute也有一个Roles参数,可仅允许某些用户调用操作。


我不确定我理解你的回答。当你说“实际认证”时,你是指在那里实际设置AuthCookie吗?那么登录表单本身在提交后会做什么呢? - BFree
据我理解,MembershipProvider 的 ValidateUser 不应该经常被调用 - 一旦用户登录,他就会收到一个身份验证 cookie,以后将使用它。但是,要获取用户对象,您必须访问数据库(或某些缓存)。但这是您代码的要求。我认为 MembershipProvider 设计得不太好,手动完成有很多好处。 - mnemosyn
现在你在我写评论的时候改变了你的评论 :) OnAuthorization 会为每个请求调用,即使用户已经(通过表单)进行了身份验证;你只是为了获取用户对象而访问数据库。 - mnemosyn
抱歉 :) 每次请求都击中数据库听起来不太理想。更重要的是,我仍然不清楚//实际身份验证部分应该是什么样子。在我的登录表单中,我会调用MembershipProvider.Validate()(或者像你建议的那样使用一些自定义内容),但是在OnAuthorization中的每个请求中应该发生什么? - BFree
OnAuthorization仅从数据库中获取用户对象,因此您已设置好用户。您可能需要在每个请求中使用用户--例如,确保用户现在被允许进行特定的请求。缓存当然是一个好主意。然后,您可以随时安全地访问HttpContext.User。实际的身份验证(我没有编写)是在表单提交时发生的,并调用FormsAuthentication.SetAuthCookie。 - mnemosyn

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