如何重载 UserManager.AddToRoleAsync(string userId, string role).



我正在使用 Asp.net 身份框架2.1。我实现了自定义的应用程序用户,应用程序角色,应用程序用户角色,因为我想添加对多租户的支持,即每个用户属于不同的公司,但我在所有这些公司中有3个角色,他们是用户,管理员和审批者。

My ApplicationUserRole 派生自 IdentityUserRole,并且还有一个属性:CompanyId。此属性将指示用户在此特定公司中的角色。我的这些自定义类的代码附在底部。

我的问题是当我尝试覆盖应用程序用户管理器(是的,它也派生自用户管理器)的AddToRoleAsyncIsInRoleAsync,我不知道如何处理新的CompanyId,看起来现有函数没有收到这些公司ID(或租户ID)。

然后,当我尝试在包含 companyId 的情况下重载这些函数时,我在 ApplicatoinUserManager 及其基类中都找不到数据库上下文。

我是否在将租户 ID/公司 ID 添加到应用程序角色的正确轨道上?

我已经参考了这个答案:SO 链接,以及 Web API 和身份 2.0 blog.ASP.NET - 自定义身份模型和实现基于角色的授权

我的身份模型:

public class ApplicationUserLogin : IdentityUserLogin<string> { }
public class ApplicationUserClaim : IdentityUserClaim<string> 
{
}
public class ApplicationUserRole : IdentityUserRole<string> 
{
    public string CompanyId { get; set; }
}
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser<string, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>//, IAppUser
{
    public ApplicationUser()
    {
        this.Id = Guid.NewGuid().ToString();
    }
    public virtual string CompanyId { get; set; }
    public virtual List<CompanyEntity> Company { get; set; }
    public DateTime CreatedOn { get; set; }
    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(ApplicationUserManager manager, string authenticationType)
    {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
        // Add custom user claims here
        return userIdentity;
    }
}
// Must be expressed in terms of our custom UserRole:
public class ApplicationRole : IdentityRole<string, ApplicationUserRole>
{
    public ApplicationRole() {}
    public ApplicationRole(string name) : this()
    {
        this.Name = name;
    }
    // Add any custom Role properties/code here
    public string Description { get; set; }
}
// Most likely won't need to customize these either, but they were needed because we implemented
// custom versions of all the other types:
public class ApplicationUserStore: UserStore<ApplicationUser, ApplicationRole, string,ApplicationUserLogin, ApplicationUserRole,ApplicationUserClaim>, IUserStore<ApplicationUser, string>, IDisposable
{
    public ApplicationUserStore()
        : this(new IdentityDbContext())
    {
        base.DisposeContext = true;
    }
    public ApplicationUserStore(DbContext context)
        : base(context)
    {
    }
}
public class ApplicationRoleStore
: RoleStore<ApplicationRole, string, ApplicationUserRole>,
IQueryableRoleStore<ApplicationRole, string>,
IRoleStore<ApplicationRole, string>, IDisposable
{
    public ApplicationRoleStore()
        : base(new IdentityDbContext())
    {
        base.DisposeContext = true;
    }
    public ApplicationRoleStore(DbContext context)
        : base(context)
    {
    }
}

我的身份配置:

public class ApplicationUserManager
    : UserManager<ApplicationUser, string>
{
    public ApplicationUserManager(IUserStore<ApplicationUser, string> store)
        : base(store) { }
    public static ApplicationUserManager Create(
        IdentityFactoryOptions<ApplicationUserManager> options,
        IOwinContext context)
    {
        var manager = new ApplicationUserManager(
            new UserStore<ApplicationUser, ApplicationRole, string,
                ApplicationUserLogin, ApplicationUserRole,
                ApplicationUserClaim>(context.Get<ApplicationDbContext>()));
        // Configure validation logic for usernames
        manager.UserValidator = new UserValidator<ApplicationUser>(manager)
        {
            AllowOnlyAlphanumericUserNames = false,
            RequireUniqueEmail = false
        };
        // Configure validation logic for passwords
        manager.PasswordValidator = new PasswordValidator
        {
            RequiredLength = 6,
            //RequireNonLetterOrDigit = true,
            //RequireDigit = true,
            //RequireLowercase = true,
            //RequireUppercase = true,
        };
        var dataProtectionProvider = options.DataProtectionProvider;
        if (dataProtectionProvider != null)
        {
            manager.UserTokenProvider =
                new DataProtectorTokenProvider<ApplicationUser>(
                    dataProtectionProvider.Create("ASP.NET Identity"));
        }
        // add sms and email service provider
        manager.SmsService = new EMaySmsServiceProvider();
        manager.EmailService = new ConcordyaEmailServiceProvider();
        return manager;
    }
    public string GetCurrentCompanyId(string userName)
    {
        var user = this.FindByName(userName);
        if (user == null)
            return string.Empty;
        var currentCompany = string.Empty;
        if (user.Claims.Count > 0)
        {
            currentCompany = user.Claims.Where(c => c.ClaimType == ConcordyaPayee.Core.Common.ConcordyaClaimTypes.CurrentCompanyId).FirstOrDefault().ClaimValue;
        }
        else
        {
            currentCompany = user.CurrentCompanyId;
        }
        return currentCompany;
    }
    public override Task<IdentityResult> AddToRoleAsync(string userId, string role, string companyId)
    { 
        return base.AddToRoleAsync(userId, role);
    }
    #region overrides for unit tests
    public override Task<bool> CheckPasswordAsync(ApplicationUser user, string password)
    {
        return base.CheckPasswordAsync(user, password);
    }
    public override Task<ApplicationUser> FindByNameAsync(string userName)
    {
        return base.FindByNameAsync(userName);
    }
    #endregion
}
public class ApplicationRoleManager : RoleManager<ApplicationRole>
{
    public ApplicationRoleManager(IRoleStore<ApplicationRole, string> roleStore)
        : base(roleStore)
    {
    }
    public static ApplicationRoleManager Create(
        IdentityFactoryOptions<ApplicationRoleManager> options,
        IOwinContext context)
    {
        return new ApplicationRoleManager(
            new ApplicationRoleStore(context.Get<ApplicationDbContext>()));
    }
}

首先,我要感谢你把它带到这一步。它为我的多租户角色解决方案提供了一个良好的开端。我不确定我是否 100% 正确,但这对我有用。

首先,您不能覆盖任何"RoleAsync"方法,但可以重载它们。其次,UserStore 有一个名为"Context"的属性,可以将其设置为您的 DbContext。

我不得不在我的UserStore和UserManager扩展类中重载"RoleAsyc"方法。以下是每个示例,可帮助您开始:

我的用户商店

    public class MyUserStore : UserStore<MyUser, MyRole, String, IdentityUserLogin, MyUserRole, IdentityUserClaim> {
        public MyUserStore(MyDbContext dbContext) : base(dbContext) { }
        public Task AddToRoleAsync(MyUser user, MyCompany company, String roleName) {
            MyRole role = null;
            try
            {
                role = Context.Set<MyRole>().Where(mr => mr.Name == roleName).Single();
            }
            catch (Exception ex)
            {
                throw ex;
            }
            Context.Set<MyUserRole>().Add(new MyUserRole {
                Company = company,
                RoleId = role.Id,
                UserId = user.Id
            });
            return Context.SaveChangesAsync();
        }
    }

我的用户管理器

    public class MyUserManager : UserManager<MyUser, String>
    {
        private MyUserStore _store = null;
        public MyUserManager(MyUserStore store) : base(store)
        {
            _store = store;
        }
        public Task<IList<String>> GetRolesAsync(String userId, int companyId)
        {
            MyUser user = _store.Context.Set<MyUser>().Find(new object[] { userId });
            MyCompany company = _store.Context.Set<MyCompany>().Find(new object[] { companyId });
            if (null == user)
            {
                throw new Exception("User not found");
            }
            if (null == company)
            {
                throw new Exception("Company not found");
            }
            return _store.GetRolesAsync(user, company);
        }
    }

从这里开始发生了一些可怕的事情,我不知道更好的方法来管理它们。

  1. HttpContext 中的用户"IsInRole"方法将起作用,但它不会对租户敏感,因此您无法再使用它。
  2. 如果您使用"授权"属性,则"可怕的事情 1"的相同想法也适用,但在这里您可以扩展它并使您的系统满意。示例如下:

MyAuthorizeAttribute

    public class MyAuthorizeAttribute : AuthorizeAttribute {
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            if (null == httpContext)
            {
                throw new ArgumentNullException("httpContext");
            }
            HttpSessionStateBase session = httpContext.Session;
            IList<String> authorizedRoleNames = Roles.Split(',').Select(r => r.Trim()).ToList();
            if (!httpContext.User.Identity.IsAuthenticated)
            {
                return false;
            }
            if (null == session["MyAuthorize.CachedUsername"])
            {
                session["MyAuthorize.CachedUsername"] = String.Empty;
            }
            if (null == session["MyAuthorize.CachedCompanyId"])
            {
                session["MyAuthorize.CachedCompanyId"] = -1;
            }
            if (null == session["MyAuthorize.CachedUserCompanyRoleNames"])
            {
                session["MyAuthorize.CachedUserCompanyRoleNames"] = new List<String>();
            }
            String cachedUsername = session["MyAuthorize.CachedUsername"].ToString();
            int cachedCompanyId = (int)session["MyAuthorize.CachedCompanyId"];
            IList<String> cachedUserAllRoleNames = (IList<String>)session["MyAuthorize.CachedUserAllRoleNames"];
            IPrincipal currentUser = httpContext.User;
            String currentUserName = currentUser.Identity.Name;
            int currentCompanyId = (int)session["CurrentCompanyId"];//Get this your own way! I used the Session in the HttpContext.
            using (MyDbContext db = MyDbContext.Create())
            {
                try
                {
                    MyUser mUser = null;
                    ICollection<String> tmpRoleIds = new List<String>();
                    if (cachedUsername != currentUserName)
                    {
                        session["MyAuthorize.CachedUsername"] = cachedUsername = String.Empty;
                        //Reload everything
                        mUser = db.Users.Where(u => u.Username == currentUserName).Single();
                        session["MyAuthorize.CachedUsername"] = currentUserName;
                        session["MyAuthorize.CachedCompanyId"] = cachedCompanyId = -1; //Force Company Reload
                        cachedUserCompanyRoleNames.Clear();
                    }
                    if (cachedUserCompanyRoleNames.Count != db.Users.Where(u => u.Username == currentUserName).Single().Roles.Select(r => r.RoleId).ToList().Count)
                    {
                        cachedUserCompanyRoleNames.Clear();
                        if (0 < currentCompanyId)
                        {
                            if(null == mUser)
                            {
                                mUser = db.Users.Where(u => u.Username == cachedUsername).Single();
                            }
                            tmpRoleIds = mUser.Roles.Where(r => r.Company.Id == currentCompanyId).Select(r => r.RoleId).ToList();
                            session["MyAuthorize.CachedUserCompanyRoleNames"] = cachedUserCompanyRoleNames = db.Roles.Where(r => tmpRoleIds.Contains(r.Id)).Select(r => r.Name).ToList();
                            session["MyAuthorize.CachedCompanyId"] = cachedCompanyId = currentCompanyId;
                        }
                    }
                    if (cachedCompanyId != currentCompanyId)
                    {
                        cachedUserCompanyRoleNames.Clear();
                        //Reload company roles
                        if (0 < currentCompanyId)
                        {
                            if(null == mUser)
                            {
                                mUser = db.Users.Where(u => u.Username == cachedUsername).Single();
                            }
                            tmpRoleIds = mUser.Roles.Where(r => r.Company.Id == currentCompanyId).Select(r => r.RoleId).ToList();
                            session["MyAuthorize.CachedUserCompanyRoleNames"] = cachedUserCompanyRoleNames = db.Roles.Where(r => tmpRoleIds.Contains(r.Id)).Select(r => r.Name).ToList();
                            session["MyAuthorize.CachedCompanyId"] = cachedCompanyId = currentCompanyId;
                        }
                    }
                }
                catch (Exception ex)
                {
                    return false;
                }
            }
            if (0 >= authorizedRoleNames.Count)
            {
                return true;
            }
            else
            {
                return cachedUserCompanyRoleNames.Intersect(authorizedRoleNames).Any();
            }
        }
    }
最后,正如我所说,我

不确定这是否是最好的方法,但它对我有用。现在,在整个系统中,请确保在处理角色时使用重载方法。我还在考虑在我编写的 MVC BaseController 中缓存角色,以便我可以在所有 MVC 视图中获得与 User.IsInRole 类似的功能。

相关内容

  • 没有找到相关文章

最新更新