使用自定义表和 Entity Framework 核心的身份核心角色授权
Identity Core role authorization with custom Tables and Entity Framework Core
我快到了,我使用 Core Identity 实现了我的自定义身份验证,因为我已经有一些 table 与用户、角色和一个加入 table 与角色分配给用户。
不要介意错误的模型 属性 名称,遗憾的是我不得不处理一个设计非常糟糕的数据库,其中没有保存加密的密码(我做了一个解决方法,你可以在下面的代码中看到,为了跳过密码散列并且它有效)。
我也从一个非常简单的角色硬编码测试开始,但我无法访问该代码。
我将我的 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();
}
}
}
然后我实现了一个 class 来跳过密码散列:
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>().'
到现在为止,我真的对网络上分散的信息感到不知所措。
有什么帮助吗?非常感谢您的宝贵时间!
我找到了解决办法。这既是与缓存相关的问题,也是与启动设置有关的问题。
上面的所有代码都有效,我必须在 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 的数据库上下文对象,它包装了我的数据库,其中包含我的三个用户 tables
- Geutenti:用户 table,以及所有相关数据(用户名、密码、电子邮件等)
- Geruoliz:角色 table
- UserStore 和 RoleStore:实现那些通知 Core Identity 如何访问用户相关数据以进行身份验证和授权的接口的对象
- NoPasswordHasher:跳过密码散列的解决方法。 (声明:这引入了一个安全漏洞,你永远不应该按原样保存密码,而是保存它们的哈希值)
我必须 logout/erase cookie 并重新启动项目,登录等!
我快到了,我使用 Core Identity 实现了我的自定义身份验证,因为我已经有一些 table 与用户、角色和一个加入 table 与角色分配给用户。
不要介意错误的模型 属性 名称,遗憾的是我不得不处理一个设计非常糟糕的数据库,其中没有保存加密的密码(我做了一个解决方法,你可以在下面的代码中看到,为了跳过密码散列并且它有效)。
我也从一个非常简单的角色硬编码测试开始,但我无法访问该代码。 我将我的 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();
}
}
}
然后我实现了一个 class 来跳过密码散列: 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>().'
到现在为止,我真的对网络上分散的信息感到不知所措。 有什么帮助吗?非常感谢您的宝贵时间!
我找到了解决办法。这既是与缓存相关的问题,也是与启动设置有关的问题。 上面的所有代码都有效,我必须在 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 的数据库上下文对象,它包装了我的数据库,其中包含我的三个用户 tables
- Geutenti:用户 table,以及所有相关数据(用户名、密码、电子邮件等)
- Geruoliz:角色 table
- UserStore 和 RoleStore:实现那些通知 Core Identity 如何访问用户相关数据以进行身份验证和授权的接口的对象
- NoPasswordHasher:跳过密码散列的解决方法。 (声明:这引入了一个安全漏洞,你永远不应该按原样保存密码,而是保存它们的哈希值)
我必须 logout/erase cookie 并重新启动项目,登录等!