首页 > 解决方案 > 使用自定义表和实体框架核心的身份核心角色授权

问题描述

我快到了,我使用 Core Identity 实现了我的自定义身份验证,因为我已经有一些包含用户、角色的表和一个为用户分配角色的连接表。

不要介意糟糕的模型属性名称,遗憾的是我必须处理一个设计非常糟糕的数据库,并且没有保存未加密的密码(我做了一个解决方法,如下面的代码所示,为了跳过密码散列并且它可以工作)。

我还从一个非常简单的角色硬编码测试开始,但我无法访问该代码。我将我的 DbContext 称为“OracleContext”,因此我的模型是从现有数据库中搭建的。

我将 .Net Core 3.1 与 Blazor Server 一起使用,我创建了实现最少接口数量的 UserStore 对象:UserStore.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

namespace core_identity_utenza_bms.Data
{
    public class UserStore : IUserStore<Geutenti>, IUserPasswordStore<Geutenti>, IUserRoleStore<Geutenti>
    {
        private readonly OracleContext _oracleContext;

        public UserStore(OracleContext oracleContext)
        {
            _oracleContext = oracleContext;
        }
        public void Dispose()
        {
            //throw new NotImplementedException();
        }

        public async Task<string> GetUserIdAsync(Geutenti user, CancellationToken cancellationToken)
        {
            return user.Cuser.ToString();
        }

        public async Task<string> GetUserNameAsync(Geutenti user, CancellationToken cancellationToken)
        {
            return user.Ruser;
        }

        public async Task SetUserNameAsync(Geutenti user, string userName, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public async Task<string> GetNormalizedUserNameAsync(Geutenti user, CancellationToken cancellationToken)
        {
            return user.Tuser;
        }

        public async Task SetNormalizedUserNameAsync(Geutenti user, string normalizedName, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public async Task<IdentityResult> CreateAsync(Geutenti user, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public async Task<IdentityResult> UpdateAsync(Geutenti user, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public async Task<IdentityResult> DeleteAsync(Geutenti user, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public async Task<Geutenti> FindByIdAsync(string userId, CancellationToken cancellationToken)
        {
            var userQuery =
                await _oracleContext.Geutenti.Where(
                    u =>
                        u.Cuser.ToString().Equals(userId) &&
                        u.Bstor.Equals("A") &&
                        u.Csoci.Equals("MASM")).FirstOrDefaultAsync(cancellationToken);
            return userQuery;
        }

        public async Task<Geutenti> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken)
        {
            var userQuery =
                await _oracleContext.Geutenti.Where(
                        u =>
                            u.Ruser.Contains("ARDILLOI") &&
                            u.Bstor.Equals("A") &&
                            u.Csoci.Equals("MASM"))
                    .FirstOrDefaultAsync(cancellationToken);
            return userQuery;
        }

        public async Task SetPasswordHashAsync(Geutenti user, string passwordHash, CancellationToken cancellationToken)
        {
            //throw new NotImplementedException();
        }

        public async Task<string> GetPasswordHashAsync(Geutenti user, CancellationToken cancellationToken)
        {
            return user.CpaswDuser;
        }

        public async Task<bool> HasPasswordAsync(Geutenti user, CancellationToken cancellationToken)
        {
            return !(user.CpaswDuser == null || user.CpaswDuser.Equals(string.Empty));
        }

        /*
         * UserRole
         */
        public async Task AddToRoleAsync(Geutenti user, string roleName, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public async Task RemoveFromRoleAsync(Geutenti user, string roleName, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public async Task<IList<string>> GetRolesAsync(Geutenti user, CancellationToken cancellationToken)
        {
            //var userRoles = _oracleContext.Geruxute.
            return new List<string> {"BURBERO", "BARBARO"};
        }

        public async Task<bool> IsInRoleAsync(Geutenti user, string roleName, CancellationToken cancellationToken)
        {
            return new List<string> { "BURBERO", "BARBARO" }.Contains(roleName);

        }

        public async Task<IList<Geutenti>> GetUsersInRoleAsync(string roleName, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }
    }
}

然后我实现了一个跳过密码哈希的类:NoPasswordHasher.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;

namespace core_identity_utenza_bms.Data
{
    public class NoPasswordHasher : IPasswordHasher<Geutenti>
    {
        public string HashPassword(Geutenti user, string password)
        {
            return password;
        }

        public PasswordVerificationResult VerifyHashedPassword(Geutenti user, string hashedPassword, string providedPassword)
        {
            return hashedPassword.Equals(providedPassword) ? PasswordVerificationResult.Success : PasswordVerificationResult.Failed;
        }
    }
}

我创建了一个 RoleStore.cs 文件,用于在 db 上检索我的角色:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

namespace core_identity_utenza_bms.Data
{
    public class RoleStore : IRoleStore<Geruoliz>
    {
        private readonly OracleContext _oracleContext;

        public RoleStore(OracleContext oracleContext)
        {
            _oracleContext = oracleContext;
        }
        public void Dispose()
        {
            //throw new NotImplementedException();
        }

        public async Task<IdentityResult> CreateAsync(Geruoliz role, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public async Task<IdentityResult> UpdateAsync(Geruoliz role, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public async Task<IdentityResult> DeleteAsync(Geruoliz role, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public async Task<string> GetRoleIdAsync(Geruoliz role, CancellationToken cancellationToken)
        {
            return role.Crule.ToString();
        }

        public async Task<string> GetRoleNameAsync(Geruoliz role, CancellationToken cancellationToken)
        {
            return role.Rrule;
        }

        public async Task SetRoleNameAsync(Geruoliz role, string roleName, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public async Task<string> GetNormalizedRoleNameAsync(Geruoliz role, CancellationToken cancellationToken)
        {
            return role.Rrule;
        }

        public async Task SetNormalizedRoleNameAsync(Geruoliz role, string normalizedName, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public async Task<Geruoliz> FindByIdAsync(string roleId, CancellationToken cancellationToken)
        {
            var role = await _oracleContext.Geruoliz.Where(
                    r =>
                        r.Crule.ToString().Equals(roleId) &&
                        r.Bstor.Equals("A") &&
                        r.Csoci.Equals("MASM"))
                .FirstOrDefaultAsync(cancellationToken: cancellationToken);
            return role;
        }

        public async Task<Geruoliz> FindByNameAsync(string normalizedRoleName, CancellationToken cancellationToken)
        {
            var role = await _oracleContext.Geruoliz.Where(
                    r =>
                        r.Rrule.Equals(normalizedRoleName) &&
                        r.Bstor.Equals("A") &&
                        r.Csoci.Equals("MASM"))
                .FirstOrDefaultAsync(cancellationToken: cancellationToken);
            return role;
        }
    }
}

最后我编辑了 Startup.cs:

services.AddDbContext<OracleContext>();
services
    .AddDefaultIdentity<Geutenti>()
    .AddUserStore<UserStore>();
services.AddScoped<IPasswordHasher<Geutenti>, NoPasswordHasher>();

到目前为止,我什至无法在 UserStore.GetRolesAsync 和 UserStore.IsInRoleAsync 中输入断点,因为我想我应该通过这些检查以确定视图是否可见,例如通过使用:

@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
<AuthorizeView>
    <p>sei loggato.</p>
</AuthorizeView>
<AuthorizeView Roles="BURBERO">
    <Authorized>
        Sei un burbero!
    </Authorized>
    <NotAuthorized>
        Sei una brava persona.
    </NotAuthorized>
</AuthorizeView>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}

我只在授权视图中成功,没有指定任何角色。我尝试的最后一件事是编辑服务,例如:

services.AddDbContext<OracleContext>();
services
    .AddDefaultIdentity<Geutenti>()
    .AddUserStore<UserStore>()
    .AddRoleStore<RoleStore>();
services.AddScoped<IPasswordHasher<Geutenti>, NoPasswordHasher>();

但这给了我例外:

System.InvalidOperationException: 'No RoleType was specified, try AddRoles<TRole>().'

到现在为止,老实说,我对网络上分散的信息感到不知所措。有什么帮助吗?非常感谢您的宝贵时间!

标签: asp.netentity-frameworkasp.net-identityblazorasp.net-core-identity

解决方案


我找到了路。这既是与缓存相关的问题,也是启动设置。上面的所有代码都可以工作,我必须在 Startup 中设置:

// Add identity types
services.AddDbContext<OracleContext>();
services
    .AddIdentity<Geutenti, Geruoliz>()
    .AddRoles<Geruoliz>()
    .AddDefaultTokenProviders();
// Identity Services
services.AddTransient<IUserStore<Geutenti>, UserStore>();
services.AddTransient<IRoleStore<Geruoliz>, RoleStore>();
services.AddScoped<IPasswordHasher<Geutenti>, NoPasswordHasher>();

这里的对象是:

  • OracleContext:它是来自 Entity Framework Core 的数据库上下文对象,它包装了我的数据库,其中包含我的三个用户表
  • Geutenti:用户表,包含所有相关数据(用户名、密码、电子邮件等)
  • Geruoliz:角色表
  • UserStore 和 RoleStore:实现这些接口的对象,这些接口通知 Core Identity 如何访问与用户相关的数据以进行身份​​验证和授权
  • NoPasswordHasher:跳过密码哈希的解决方法。(DESCLAIM:这引入了一个安全漏洞,您永远不应该按原样保存密码,而是保存它们的哈希值)

我不得不注销/删除 cookie 并重新启动项目,登录等等!


推荐阅读