Asp.Net 从 ApplicationUser 派生的核心不同用户模型
Asp.Net Core Different User Models to derive from ApplicationUser
我们刚刚开始学习如何编码,我们正在研究一个 Asp.Net Core 3.1 项目,我知道这是一个简单的问题,但我到处搜索但没有找到解决方案。
我们有 Admin、Client、Mentor User Models,除了来自 ApplicationUser 模型的标准 Email、Pass、First name 和 Last name 之外,它们还具有我们需要的所有额外的用户属性。
所以我们有这样的东西(你可以在模型下面找到):
AdminUserDetails ---\
ClientUserDetails ------ : ApplicationUser : IdentityUser
MentorUserDetails ---/
此刻,身份验证和授权工作正常,但所有用户都是在 EF 中的 AspNetUser Table 中创建的,但我们希望在客户端中创建每个角色 (eg.Client) table 继承了 ApplicationUser 和 IdentityUser 的所有属性。
首先,我发现 this link 从 ApplicationUser 继承模型。这是正确的做法吗?
此外,当用户注册了在表单中选择的角色时,我们希望将该用户分配给相应的 ...UserDetails 模型并在 Guid 中获取身份 ID。
以下是一些示例模型:
public class ApplicationUser : IdentityUser
{
[Required]
[MaxLength(50)]
public string FirstName { get; set; }
[Required]
[MaxLength(50)]
public string LastName { get; set; }
[NotMapped]
public string Name
{
get
{
return FirstName + " " + LastName;
}
}
[Required]
[Display(Name = "Phone Number")]
[DataType(DataType.PhoneNumber)]
public int? PhoneNumber { get; set; }
[Display(Name = "Profile Picture")]
[DataType(DataType.Upload)]
public string? ProfilePicture { get; set; }
}
public class AdministratorDetails : ApplicationUser
{
[Key]
[ForeignKey("ApplicationUser")]
public Guid Id { get; set; }
//Προσθέτω Foreign key se ola ta details APP USER
public virtual ApplicationUser ApplicationUser { get; set; }
}
启动:
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>() //Πρόσθεσα Identity role
.AddRoleManager<RoleManager<IdentityRole>>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
})
.AddIdentityServerJwt();
services.AddControllersWithViews();
services.AddRazorPages();
// In production, the React files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
//SendGrid Confirmation Email Sender
services.AddTransient<IEmailSender, EmailSender>();
services.Configure<AuthMessageSenderOptions>(Configuration);
services.AddTransient<IProfileService, IdentityClaimsProfileService>();
services.AddScoped<IJobCategoriesRepository, JobCategoryRepository>();
services.AddScoped<IMentorRepository, MentorRepository>();
services.AddScoped<IHrDetailsRepository, HrDetailsRepository>();
services.AddScoped<IClientRepository, ClientRepository>();
services.AddScoped<IJobListingsRepository, JobListingsRepository>();
services.AddScoped<IMentorOfferRepository, MentorOfferRepository>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider serviceProvider)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseCors(
options => options.WithOrigins("https://localhost:5001/")
.AllowAnyHeader()
.AllowAnyMethod()
);
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
app.UseStaticFiles();
app.UseIdentityServer();
CreateRoles(serviceProvider).Wait();
}
private async Task CreateRoles(IServiceProvider serviceProvider)
{
//adding custom roles
var RoleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
var UserManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();
string[] roleNames = { "Admin", "HR", "Mentor", "Client" };
IdentityResult roleResult;
foreach (var roleName in roleNames)
{
//creating the roles and seeding them to the database
var roleExist = await RoleManager.RoleExistsAsync(roleName);
if (!roleExist)
{
roleResult = await RoleManager.CreateAsync(new IdentityRole(roleName));
}
}
//creating a super user who could maintain the web app
var powerUser = new ApplicationUser
{
UserName = Configuration.GetSection("UserSettings")["UserEmail"],
Email = Configuration.GetSection("UserSettings")["UserEmail"],
FirstName = "PowerAdmin",
LastName = "Admin",
UserRole = "Admin",
EmailConfirmed = true
};
string UserPassword = Configuration.GetSection("UserSettings")["UserPassword"];
var _user = await UserManager.FindByEmailAsync(Configuration.GetSection("UserSettings")["UserEmail"]);
if (_user == null)
{
var createPowerUser = await UserManager.CreateAsync(powerUser, UserPassword);
if (createPowerUser.Succeeded)
{
//here we tie the new user to the "Admin" role
await UserManager.AddToRoleAsync(powerUser, "Admin");
}
}
}
public class IdentityClaimsProfileService : IProfileService
{
private readonly IUserClaimsPrincipalFactory<ApplicationUser> _claimsFactory;
private readonly UserManager<ApplicationUser> _userManager;
public IdentityClaimsProfileService(UserManager<ApplicationUser> userManager, IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory)
{
_userManager = userManager;
_claimsFactory = claimsFactory;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var sub = context.Subject.GetSubjectId();
var user = await _userManager.FindByIdAsync(sub);
var principal = await _claimsFactory.CreateAsync(user);
var roles = await _userManager.GetRolesAsync(user);
var claims = principal.Claims.ToList();
claims = claims.Where(claim => context.RequestedClaimTypes.Contains(claim.Type)).ToList();
foreach(string role in roles)
{
claims.Add(new Claim(JwtClaimTypes.Role, role));
}
context.IssuedClaims = claims;
}
public async Task IsActiveAsync(IsActiveContext context)
{
var sub = context.Subject.GetSubjectId();
var user = await _userManager.FindByIdAsync(sub);
context.IsActive = user != null;
}
}
感谢@Xin Zou 的评论,我终于意识到我必须从我的用户模型中删除 IdentityUser 继承,并且只为每个用户设置外键
[ForeignKey("ApplicationUserId")]
public ApplicationUser ApplicationUser { get; set; }
public string ApplicationUserId { get; set; }
因此创建一个 AspNetUser table 但 也 一个用户 table (根据它的角色)与 AspNetUser 的外键。这里唯一的问题是用户 table 必须让他们的道具可以为空。
我们刚刚开始学习如何编码,我们正在研究一个 Asp.Net Core 3.1 项目,我知道这是一个简单的问题,但我到处搜索但没有找到解决方案。
我们有 Admin、Client、Mentor User Models,除了来自 ApplicationUser 模型的标准 Email、Pass、First name 和 Last name 之外,它们还具有我们需要的所有额外的用户属性。
所以我们有这样的东西(你可以在模型下面找到):
AdminUserDetails ---\
ClientUserDetails ------ : ApplicationUser : IdentityUser
MentorUserDetails ---/
此刻,身份验证和授权工作正常,但所有用户都是在 EF 中的 AspNetUser Table 中创建的,但我们希望在客户端中创建每个角色 (eg.Client) table 继承了 ApplicationUser 和 IdentityUser 的所有属性。
首先,我发现 this link 从 ApplicationUser 继承模型。这是正确的做法吗?
此外,当用户注册了在表单中选择的角色时,我们希望将该用户分配给相应的 ...UserDetails 模型并在 Guid 中获取身份 ID。
以下是一些示例模型:
public class ApplicationUser : IdentityUser
{
[Required]
[MaxLength(50)]
public string FirstName { get; set; }
[Required]
[MaxLength(50)]
public string LastName { get; set; }
[NotMapped]
public string Name
{
get
{
return FirstName + " " + LastName;
}
}
[Required]
[Display(Name = "Phone Number")]
[DataType(DataType.PhoneNumber)]
public int? PhoneNumber { get; set; }
[Display(Name = "Profile Picture")]
[DataType(DataType.Upload)]
public string? ProfilePicture { get; set; }
}
public class AdministratorDetails : ApplicationUser
{
[Key]
[ForeignKey("ApplicationUser")]
public Guid Id { get; set; }
//Προσθέτω Foreign key se ola ta details APP USER
public virtual ApplicationUser ApplicationUser { get; set; }
}
启动:
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>() //Πρόσθεσα Identity role
.AddRoleManager<RoleManager<IdentityRole>>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
})
.AddIdentityServerJwt();
services.AddControllersWithViews();
services.AddRazorPages();
// In production, the React files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
//SendGrid Confirmation Email Sender
services.AddTransient<IEmailSender, EmailSender>();
services.Configure<AuthMessageSenderOptions>(Configuration);
services.AddTransient<IProfileService, IdentityClaimsProfileService>();
services.AddScoped<IJobCategoriesRepository, JobCategoryRepository>();
services.AddScoped<IMentorRepository, MentorRepository>();
services.AddScoped<IHrDetailsRepository, HrDetailsRepository>();
services.AddScoped<IClientRepository, ClientRepository>();
services.AddScoped<IJobListingsRepository, JobListingsRepository>();
services.AddScoped<IMentorOfferRepository, MentorOfferRepository>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider serviceProvider)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseCors(
options => options.WithOrigins("https://localhost:5001/")
.AllowAnyHeader()
.AllowAnyMethod()
);
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
app.UseStaticFiles();
app.UseIdentityServer();
CreateRoles(serviceProvider).Wait();
}
private async Task CreateRoles(IServiceProvider serviceProvider)
{
//adding custom roles
var RoleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
var UserManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();
string[] roleNames = { "Admin", "HR", "Mentor", "Client" };
IdentityResult roleResult;
foreach (var roleName in roleNames)
{
//creating the roles and seeding them to the database
var roleExist = await RoleManager.RoleExistsAsync(roleName);
if (!roleExist)
{
roleResult = await RoleManager.CreateAsync(new IdentityRole(roleName));
}
}
//creating a super user who could maintain the web app
var powerUser = new ApplicationUser
{
UserName = Configuration.GetSection("UserSettings")["UserEmail"],
Email = Configuration.GetSection("UserSettings")["UserEmail"],
FirstName = "PowerAdmin",
LastName = "Admin",
UserRole = "Admin",
EmailConfirmed = true
};
string UserPassword = Configuration.GetSection("UserSettings")["UserPassword"];
var _user = await UserManager.FindByEmailAsync(Configuration.GetSection("UserSettings")["UserEmail"]);
if (_user == null)
{
var createPowerUser = await UserManager.CreateAsync(powerUser, UserPassword);
if (createPowerUser.Succeeded)
{
//here we tie the new user to the "Admin" role
await UserManager.AddToRoleAsync(powerUser, "Admin");
}
}
}
public class IdentityClaimsProfileService : IProfileService
{
private readonly IUserClaimsPrincipalFactory<ApplicationUser> _claimsFactory;
private readonly UserManager<ApplicationUser> _userManager;
public IdentityClaimsProfileService(UserManager<ApplicationUser> userManager, IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory)
{
_userManager = userManager;
_claimsFactory = claimsFactory;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var sub = context.Subject.GetSubjectId();
var user = await _userManager.FindByIdAsync(sub);
var principal = await _claimsFactory.CreateAsync(user);
var roles = await _userManager.GetRolesAsync(user);
var claims = principal.Claims.ToList();
claims = claims.Where(claim => context.RequestedClaimTypes.Contains(claim.Type)).ToList();
foreach(string role in roles)
{
claims.Add(new Claim(JwtClaimTypes.Role, role));
}
context.IssuedClaims = claims;
}
public async Task IsActiveAsync(IsActiveContext context)
{
var sub = context.Subject.GetSubjectId();
var user = await _userManager.FindByIdAsync(sub);
context.IsActive = user != null;
}
}
感谢@Xin Zou 的评论,我终于意识到我必须从我的用户模型中删除 IdentityUser 继承,并且只为每个用户设置外键
[ForeignKey("ApplicationUserId")]
public ApplicationUser ApplicationUser { get; set; }
public string ApplicationUserId { get; set; }
因此创建一个 AspNetUser table 但 也 一个用户 table (根据它的角色)与 AspNetUser 的外键。这里唯一的问题是用户 table 必须让他们的道具可以为空。