六、.Net Core Web Api连接MongoDB添加ASP.NET Core Identity身份验证授权 - 自动创建管理员用户和基础用户
-
在VSCode安装包,搜索AspNetCore.Identity.Mongo,安装最新版本即可;
-
更新你的appsettings.Development.json,添加Users和Roles这两个Collection名称的值;
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "TeachingAppDatabase": { "BooksCollectionName": "books", "UsersCollectionName": "users", "RolesCollectionName": "roles", "ConnectionString": "Fill it with your MongoDB connection string", "DatabaseName": "teaching-blazor-app" }, "AllowedHosts": "*" }
-
更新配置类TeachingAppDatabaseSettings.cs,负责读取配置文件中的users和roles collections名称;
using System; namespace TeachingWebApi.Config { public class TeachingAppDatabaseSettings { public string BooksCollectionName { get; init; } public string UsersCollectionName { get; init; } public string RolesCollectionName { get; init; } public string ConnectionString { get; init; } public string DatabaseName { get; init; } } }
-
添加模型类TeachingUsers.cs和TeachingRoles.cs,控制Identity用户和角色;
using System.ComponentModel.DataAnnotations.Schema; using AspNetCore.Identity.Mongo.Model; namespace TeachingWebApi.Models.Identity { public class TeachingBlazorUser : MongoUser { public string FirstName { get; set; } public string LastName { get; set; } public string CreatedBy { get; set; } [Column(TypeName = "text")] public string ProfilePictureDataUrl { get; set; } public DateTime CreatedOn { get; set; } public string LastModifiedBy { get; set; } public DateTime? LastModifiedOn { get; set; } public bool IsDeleted { get; set; } public DateTime? DeletedOn { get; set; } public bool IsActive { get; set; } public string RefreshToken { get; set; } public DateTime RefreshTokenExpiryTime { get; set; } } }
using System; using System.ComponentModel.DataAnnotations; using AspNetCore.Identity.Mongo.Model; using MongoDB.Bson; namespace TeachingWebApi.Models.Identity { public class TeachingBlazorRole : MongoRole { public override ObjectId Id { get; set; } public string Description { get; set; } public string CreatedBy { get; set; } public DateTime CreatedOn { get; set; } public string LastModifiedBy { get; set; } public DateTime? LastModifiedOn { get; set; } public TeachingBlazorRole(string roleName, string roleDescription) : base(roleName) { Description = roleDescription; } } }
-
在Program.cs添加红色背景的代码,配置MongoDB Identity;
using AspNetCore.Identity.Mongo; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using MongoDB.Driver; using TeachingWebApi.Config; using TeachingWebApi.Data.Seeder; using TeachingWebApi.Models.Identity; using TeachingWebApi.Services; using TeachingWebApi.Utils.Data; using TeachingWebApi.Utils.Extensions; var builder = WebApplication.CreateBuilder(args); // Add services to the container. // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddControllers(); builder.Services.Configure<TeachingAppDatabaseSettings>( builder.Configuration.GetSection("TeachingAppDatabase")); var MongoDbConnectionString = builder.Configuration.GetSection("TeachingAppDatabase").Get<TeachingAppDatabaseSettings>().ConnectionString; var MongoDbUsersCollection = builder.Configuration.GetSection("TeachingAppDatabase").Get<TeachingAppDatabaseSettings>().UsersCollectionName; var MongoDbRolesCollection = builder.Configuration.GetSection("TeachingAppDatabase").Get<TeachingAppDatabaseSettings>().RolesCollectionName; builder.Services.AddSingleton<BooksService>(); // MongoDB with Identity builder.Services.AddIdentityMongoDbProvider<TeachingBlazorUser, TeachingBlazorRole>(identity => { // Password settings. identity.Password.RequireDigit = false; identity.Password.RequireLowercase = false; identity.Password.RequireNonAlphanumeric = false; identity.Password.RequireUppercase = false; identity.Password.RequiredLength = 1; identity.Password.RequiredUniqueChars = 0; // Lockout settings. identity.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5); identity.Lockout.MaxFailedAccessAttempts = 5; identity.Lockout.AllowedForNewUsers = true; // User settings. identity.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+"; identity.User.RequireUniqueEmail = false; }, mongo => { mongo.ConnectionString = MongoDbConnectionString; mongo.UsersCollection = MongoDbUsersCollection; mongo.RolesCollection = MongoDbRolesCollection; } ) .AddEntityFrameworkStores<MongoIdentityDbContext>() .AddDefaultTokenProviders(); builder.Services.AddTransient<MongoDbContext>(); builder.Services.AddTransient<MongoIdentityDbContext>(); builder.Services .AddTransient<DatabaseSeeder>() .AddDbContext<MongoIdentityDbContext>(options => options .UseMongoDB(new MongoClient(MongoDbConnectionString), "teaching_blazor_app")); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.Initialize(builder.Configuration); app.UseHttpsRedirection(); var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; app.MapGet("/weatherforecast", () => { var forecast = Enumerable.Range(1, 5).Select(index => new WeatherForecast ( DateOnly.FromDateTime(DateTime.Now.AddDays(index)), Random.Shared.Next(-20, 55), summaries[Random.Shared.Next(summaries.Length)] )) .ToArray(); return forecast; }) .WithName("GetWeatherForecast") .WithOpenApi(); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello From ASP.NET Core Web API"); }); endpoints.MapGet("/Resource1", async context => { await context.Response.WriteAsync("Hello From ASP.NET Core Web API Resource1"); }); endpoints.MapControllerRoute( name: "Admin", pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}"); endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); app.Run(); record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) { public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); }
-
新建常量类RoleConstants.cs和UserConstants.cs,提供默认密码和角色信息:
namespace TeachingWebApi.Utils.Constants { public static class UserConstants { public const string DefaultPassword = "123Pa$$word!"; } }
namespace TeachingWebApi.Utils.Constants { public static class RoleConstants { public const string AdministratorRole = "Administrator"; public const string BasicRole = "Basic"; public const string DefaultPassword = "123Pa$$word!"; } }
-
安装包Microsoft.EntityFrameworkCore.Relational 和 Microsoft.AspNetCore.Identity.EntityFrameworkCore,选择8.x版本即可;
-
在你的ConnectionString里的.net/后加入你的数据库名称,这样会默认使用你的数据库:
-
新建MongoIdentityDbContext.cs,创建Identity的Users和Roles表:
using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using MongoDB.Bson; using MongoDB.Driver; using MongoDB.EntityFrameworkCore.Extensions; using System.Linq; using System.Threading; using System.Threading.Tasks; using TeachingWebApi.Models.Identity; namespace TeachingWebApi.Utils.Data { public class MongoIdentityDbContext : DbContext { public MongoIdentityDbContext(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder builder) { foreach (var property in builder.Model.GetEntityTypes() .SelectMany(t => t.GetProperties()) .Where(p => p.ClrType == typeof(decimal) || p.ClrType == typeof(decimal?))) { property.SetColumnType("decimal(18,2)"); } foreach (var property in builder.Model.GetEntityTypes() .SelectMany(t => t.GetProperties()) .Where(p => p.Name is "LastModifiedBy" or "CreatedBy")) { property.SetColumnType("nvarchar(128)"); } base.OnModelCreating(builder); builder.Entity<TeachingBlazorUser>(entity => { entity.ToTable(name: "Users", "Identity"); entity.Property(e => e.Id).ValueGeneratedOnAdd(); }); builder.Entity<TeachingBlazorRole>(entity => { entity.ToTable(name: "Roles", "Identity").HasKey(x => x.Id); }); builder.Entity<IdentityUserClaim<ObjectId>>(entity => { entity.ToTable("UserClaims", "Identity"); }); } } }
-
更新DatabaseSeeder.cs,加入自动创建管理员和基础用户的代码:
using Microsoft.AspNetCore.Identity; using MongoDB.Driver; using TeachingWebApi.Utils.Data; using TeachingWebApi.Models.Identity; using TeachingWebApi.Utils.Constants; namespace TeachingWebApi.Data.Seeder { public class DatabaseSeeder { private readonly MongoDbContext _dbContext; private readonly UserManager<TeachingBlazorUser> _userManager; private readonly RoleManager<TeachingBlazorRole> _roleManager; public DatabaseSeeder( MongoDbContext dbContext, UserManager<TeachingBlazorUser> userManager, RoleManager<TeachingBlazorRole> roleManager) { _dbContext = dbContext; _userManager = userManager; _roleManager = roleManager; } public void Initialize() { InitializeCollection(); AddAdministrator(); AddBasicUser(); } public async void InitializeCollection() { await CreateCollection("books"); await CreateCollection("users"); await CreateCollection("roles"); } private void AddAdministrator() { Task.Run(async () => { //Check if Role Exists var adminRole = new TeachingBlazorRole(RoleConstants.AdministratorRole, "Administrator role with full permissions"); var adminRoleInDb = await _roleManager.FindByNameAsync(RoleConstants.AdministratorRole); if (adminRoleInDb == null) { await _roleManager.CreateAsync(adminRole); adminRoleInDb = await _roleManager.FindByNameAsync(RoleConstants.AdministratorRole); } // Check if User Exists var superUser = new TeachingBlazorUser { FirstName = "SuperUser", LastName = "SuperUser", Email = "SuperUser@163.com", UserName = "SuperUser", EmailConfirmed = true, PhoneNumberConfirmed = true, CreatedOn = DateTime.Now, IsActive = true }; var superUserInDb = await _userManager.FindByEmailAsync(superUser.Email); if (superUserInDb == null) { await _userManager.CreateAsync(superUser, UserConstants.DefaultPassword); var result = await _userManager.AddToRoleAsync(superUser, RoleConstants.AdministratorRole); // UpdateClaimsAsync(superUser.Id.ToString()); } }).GetAwaiter().GetResult(); } private void AddBasicUser() { Task.Run(async () => { //Check if Role Exists var basicRole = new TeachingBlazorRole(RoleConstants.BasicRole, "Basic role with default permissions"); var basicRoleInDb = await _roleManager.FindByNameAsync(RoleConstants.BasicRole); if (basicRoleInDb == null) { await _roleManager.CreateAsync(basicRole); } //Check if User Exists var basicUser = new TeachingBlazorUser { FirstName = "BasicUser", LastName = "BasicUser", Email = "basicuser@163.com", UserName = "BasicUser", EmailConfirmed = true, PhoneNumberConfirmed = true, CreatedOn = DateTime.Now, IsActive = true }; var basicUserInDb = await _userManager.FindByEmailAsync(basicUser.Email); if (basicUserInDb == null) { await _userManager.CreateAsync(basicUser, UserConstants.DefaultPassword); await _userManager.AddToRoleAsync(basicUser, RoleConstants.BasicRole); } }).GetAwaiter().GetResult(); } private async Task CreateCollection(string collectionName) { if (!CollectionExists(_dbContext._db, collectionName)) { await _dbContext._db.CreateCollectionAsync(collectionName, new CreateCollectionOptions { Capped = false }); Console.WriteLine("Collection created!"); } } private bool CollectionExists(IMongoDatabase database, string collectionName) { return database.ListCollectionNames().ToList().Contains(collectionName); } } }
-
运行Web Api应用,可以看到MongoDB上你的数据库已自动新增了users和roles的collections,并且可以看到管理员和基础用户已经添加到users表。