06添加身份验证
06添加身份验证
Domain.Shared
添加Nugget包:
- Volo.Abp.PermissionManagement.Domain.Shared
- Volo.Abp.Identity.Domain.Shared
- Volo.Abp.OpenIddict.Domain.Shared
DomainSharedModule.cs
添加以下依赖:
- typeof(AbpIdentityDomainSharedModule)
- typeof(AbpPermissionManagementDomainSharedModule)
- typeof(AbpOpenIddictDomainSharedModule)
代码如下:
// 添加依赖 本地化模组 [DependsOn( typeof(AbpLocalizationModule), typeof(AbpIdentityDomainSharedModule), typeof(AbpPermissionManagementDomainSharedModule), typeof(AbpOpenIddictDomainSharedModule) )] public class DomainSharedModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { ... } }
添加常量类DomainConsts
public static class DomainConsts { // 用于系统管理员权限验证 public const string SystemAdminClaimKey = "User_Type"; // 用于系统管理员权限验证 public const string SystemAdminClaimValue = "SystemAdmin"; }
添加常量类MultiTenancyConsts
public static class MultiTenancyConsts { /* Enable/disable multi-tenancy easily in a single point. * If you will never need to multi-tenancy, you can remove * related modules and code parts, including this file. */ public const bool IsEnabled = true; }
Domain
添加Nugget包:
- Volo.Abp.Identity.Domain
- Volo.Abp.OpenIddict.Domain
- Volo.Abp.PermissionManagement.Domain
- Volo.Abp.PermissionManagement.Domain.OpenIddict
DomainModule.cs
添加以下依赖:
- typeof(AbpIdentityDomainModule)
- typeof(AbpPermissionManagementDomainModule)
- typeof(AbpOpenIddictDomainModule)
- typeof(AbpPermissionManagementDomainOpenIddictModule)
代码如下:
[DependsOn( typeof(AbpDddDomainModule), typeof(DomainSharedModule), typeof(AbpIdentityDomainModule), typeof(AbpPermissionManagementDomainModule), typeof(AbpOpenIddictDomainModule), typeof(AbpPermissionManagementDomainOpenIddictModule) )] public class DomainModule:AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { ... } }
自定义系统管理员权限值提供类
添加类SystemAdminPermissionValueProvider
,代码如下:
public class SystemAdminPermissionValueProvider : PermissionValueProvider { public SystemAdminPermissionValueProvider(IPermissionStore permissionStore) : base(permissionStore) { } public override string Name => DomainConsts.SystemAdminClaimValue; public override Task<PermissionGrantResult> CheckAsync(PermissionValueCheckContext context) { // 如果用户类型为系统管理员,则返回授予权限,否则返回未知权限,交给后面的PermissionValueProvider子类进行验证 if (context.Principal?.FindFirst(DomainConsts.SystemAdminClaimKey)?.Value == Name) { return Task.FromResult(PermissionGrantResult.Granted); } return Task.FromResult(PermissionGrantResult.Undefined); } public override Task<MultiplePermissionGrantResult> CheckAsync(PermissionValuesCheckContext context) { var result = new MultiplePermissionGrantResult(); // 如果用户类型为系统管理员,则返回授予权限,否则返回未知权限,交给后面的PermissionValueProvider子类进行验证 if(context.Principal?.FindFirst(DomainConsts.SystemAdminClaimKey)?.Value == Name) { result.Result.Add(Name, PermissionGrantResult.Granted); } else { result.Result.Add(Name,PermissionGrantResult.Undefined); } return Task.FromResult(result); } }
在DomainModule
类中配置添加该权限值体用类,完整代码如下:
[DependsOn( typeof(AbpDddDomainModule), typeof(DomainSharedModule), typeof(AbpIdentityDomainModule), typeof(AbpPermissionManagementDomainModule), typeof(AbpOpenIddictDomainModule), typeof(AbpPermissionManagementDomainOpenIddictModule) )] public class DomainModule:AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { base.ConfigureServices(context); Configure<AbpPermissionOptions>(options => { options.ValueProviders.Add<SystemAdminPermissionValueProvider>(); // 添加SystemAdminPermissionValueProvider 权限值提供类配置 }); } }
添加数据种子
添加数据库迁移服务
public interface IDbSchemaMigrator { Task MigrateAsync(); }
public class DbMigrationService : ITransientDependency { public ILogger<DbMigrationService> Logger { get; set; } private readonly IDataSeeder _dataSeeder; private readonly IEnumerable<IDbSchemaMigrator> _dbSchemaMigrators; public DbMigrationService( IDataSeeder dataSeeder, IEnumerable<IDbSchemaMigrator> dbSchemaMigrators, ICurrentTenant currentTenant) { _dataSeeder = dataSeeder; _dbSchemaMigrators = dbSchemaMigrators; Logger = NullLogger<DbMigrationService>.Instance; } public async Task MigrateAsync() { var initialMigrationAdded = AddInitialMigrationIfNotExist(); if (initialMigrationAdded) { return; } Logger.LogInformation("Started database migrations..."); await MigrateDatabaseSchemaAsync(); await SeedDataAsync(); Logger.LogInformation($"Successfully completed host database migrations."); Logger.LogInformation("Successfully completed all database migrations."); Logger.LogInformation("You can safely end this process..."); } private async Task MigrateDatabaseSchemaAsync() { Logger.LogInformation( $"Migrating schema for host database..."); foreach (var migrator in _dbSchemaMigrators) { await migrator.MigrateAsync(); } } private async Task SeedDataAsync() { Logger.LogInformation($"Executing host database seed..."); await _dataSeeder.SeedAsync(new DataSeedContext() .WithProperty(IdentityDataSeedContributor.AdminEmailPropertyName, IdentityDataSeedContributor.AdminEmailDefaultValue) .WithProperty(IdentityDataSeedContributor.AdminPasswordPropertyName, IdentityDataSeedContributor.AdminPasswordDefaultValue) ); } private bool AddInitialMigrationIfNotExist() { try { if (!DbMigrationsProjectExists()) { return false; } } catch (Exception) { return false; } try { if (!MigrationsFolderExists()) { AddInitialMigration(); return true; } else { return false; } } catch (Exception e) { Logger.LogWarning("Couldn't determinate if any migrations exist : " + e.Message); return false; } } private bool DbMigrationsProjectExists() { var dbMigrationsProjectFolder = GetEntityFrameworkCoreProjectFolderPath(); return dbMigrationsProjectFolder != null; } private bool MigrationsFolderExists() { var dbMigrationsProjectFolder = GetEntityFrameworkCoreProjectFolderPath(); return Directory.Exists(Path.Combine(dbMigrationsProjectFolder, "Migrations")); } private void AddInitialMigration() { Logger.LogInformation("Creating initial migration..."); string argumentPrefix; string fileName; if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { argumentPrefix = "-c"; fileName = "/bin/bash"; } else { argumentPrefix = "/C"; fileName = "cmd.exe"; } var procStartInfo = new ProcessStartInfo(fileName, $"{argumentPrefix} \"abp create-migration-and-run-migrator \"{GetEntityFrameworkCoreProjectFolderPath()}\"\"" ); try { Process.Start(procStartInfo); } catch (Exception) { throw new Exception("Couldn't run ABP CLI..."); } } private string GetEntityFrameworkCoreProjectFolderPath() { var slnDirectoryPath = GetSolutionDirectoryPath(); if (slnDirectoryPath == null) { throw new Exception("Solution folder not found!"); } var srcDirectoryPath = Path.Combine(slnDirectoryPath, "src", "shoudongdajian"); return Directory.GetDirectories(srcDirectoryPath) .FirstOrDefault(d => d.EndsWith("EntityFrameworkCore")); } private string GetSolutionDirectoryPath() { var currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory()); while (Directory.GetParent(currentDirectory.FullName) != null) { currentDirectory = Directory.GetParent(currentDirectory.FullName); if (Directory.GetFiles(currentDirectory.FullName).FirstOrDefault(f => f.EndsWith(".sln")) != null) { return currentDirectory.FullName; } } return null; } }
/* This is used if database provider does't define * IStudyAbpDbSchemaMigrator implementation. */ public class NullDbSchemaMigrator : IDbSchemaMigrator, ITransientDependency { public Task MigrateAsync() { return Task.CompletedTask; } }
添加身份认证数据种子
/* * 创建运行应用程序所需的初始数据 * 并使客户端到服务器通信成为可能。 */ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDependency { private readonly IConfiguration _configuration; private readonly IAbpApplicationManager _applicationManager; private readonly IOpenIddictScopeManager _scopeManager; private readonly IPermissionDataSeeder _permissionDataSeeder; private readonly IStringLocalizer<OpenIddictResponse> L; public OpenIddictDataSeedContributor( IConfiguration configuration, IAbpApplicationManager applicationManager, IOpenIddictScopeManager scopeManager, IPermissionDataSeeder permissionDataSeeder, IStringLocalizer<OpenIddictResponse> l) { _configuration = configuration; _applicationManager = applicationManager; _scopeManager = scopeManager; _permissionDataSeeder = permissionDataSeeder; L = l; } [UnitOfWork] public virtual async Task SeedAsync(DataSeedContext context) { await CreateScopesAsync(); await CreateApplicationsAsync(); } /// <summary> /// 添加身份认证作用域 /// </summary> /// <returns></returns> private async Task CreateScopesAsync() { if (await _scopeManager.FindByNameAsync("StudyAbp") == null) { await _scopeManager.CreateAsync(new OpenIddictScopeDescriptor { Name = "StudyAbp", DisplayName = "StudyAbp API", Resources = { "StudyAbp" } }); } if (await _scopeManager.FindByNameAsync("StudyAcme") == null) { await _scopeManager.CreateAsync(new OpenIddictScopeDescriptor { Name = "StudyAcme", DisplayName = "StudyAcme API", Resources = { "StudyAcme" } }); } } /// <summary> /// 添加身份认证应用程序 /// </summary> /// <returns></returns> private async Task CreateApplicationsAsync() { var commonScopes = new List<string> { OpenIddictConstants.Permissions.Scopes.Address, OpenIddictConstants.Permissions.Scopes.Email, OpenIddictConstants.Permissions.Scopes.Phone, OpenIddictConstants.Permissions.Scopes.Profile, OpenIddictConstants.Permissions.Scopes.Roles, "StudyAbp" }; var configurationSection = _configuration.GetSection("OpenIddict:Applications"); //Web Client var webClientId = configurationSection["StudyAbp_Web:ClientId"]; if (!webClientId.IsNullOrWhiteSpace()) { var webClientRootUrl = configurationSection["StudyAbp_Web:RootUrl"].EnsureEndsWith('/'); /* StudyAbp_Web client is only needed if you created a tiered * solution. Otherwise, you can delete this client. */ await CreateApplicationAsync( name: webClientId, type: OpenIddictConstants.ClientTypes.Confidential, consentType: OpenIddictConstants.ConsentTypes.Implicit, displayName: "Web Application", secret: configurationSection["StudyAbp_Web:ClientSecret"] ?? "1q2w3e*", grantTypes: new List<string> //Hybrid flow { OpenIddictConstants.GrantTypes.AuthorizationCode, OpenIddictConstants.GrantTypes.Implicit }, scopes: commonScopes, redirectUri: $"{webClientRootUrl}signin-oidc", clientUri: webClientRootUrl, postLogoutRedirectUri: $"{webClientRootUrl}signout-callback-oidc" ); } //Console Test / Angular Client var consoleAndAngularClientId = configurationSection["StudyAbp_App:ClientId"]; if (!consoleAndAngularClientId.IsNullOrWhiteSpace()) { var consoleAndAngularClientRootUrl = configurationSection["StudyAbp_App:RootUrl"]?.TrimEnd('/'); await CreateApplicationAsync( name: consoleAndAngularClientId, type: OpenIddictConstants.ClientTypes.Public, consentType: OpenIddictConstants.ConsentTypes.Implicit, displayName: "Console Test / Angular Application", secret: null, grantTypes: new List<string> { OpenIddictConstants.GrantTypes.AuthorizationCode, OpenIddictConstants.GrantTypes.Password, OpenIddictConstants.GrantTypes.ClientCredentials, OpenIddictConstants.GrantTypes.RefreshToken }, scopes: commonScopes, redirectUri: consoleAndAngularClientRootUrl, clientUri: consoleAndAngularClientRootUrl, postLogoutRedirectUri: consoleAndAngularClientRootUrl ); } // Vue Client var vueClientId = configurationSection["StudyAbp_Vue_App:ClientId"]; if (!vueClientId.IsNullOrWhiteSpace()) { var vueClientRootUrl = configurationSection["StudyAbp_Vue_App:RootUrl"]?.TrimEnd('/'); await CreateApplicationAsync( name: vueClientId, type: OpenIddictConstants.ClientTypes.Public, consentType: OpenIddictConstants.ConsentTypes.Implicit, displayName: "Vue Application", secret: null, grantTypes: new List<string> { OpenIddictConstants.GrantTypes.AuthorizationCode, OpenIddictConstants.GrantTypes.Password, OpenIddictConstants.GrantTypes.ClientCredentials, OpenIddictConstants.GrantTypes.RefreshToken }, scopes: commonScopes, redirectUri: vueClientRootUrl, clientUri: vueClientRootUrl, postLogoutRedirectUri: vueClientRootUrl ); } // Blazor Client var blazorClientId = configurationSection["StudyAbp_Blazor:ClientId"]; if (!blazorClientId.IsNullOrWhiteSpace()) { var blazorRootUrl = configurationSection["StudyAbp_Blazor:RootUrl"].TrimEnd('/'); await CreateApplicationAsync( name: blazorClientId, type: OpenIddictConstants.ClientTypes.Public, consentType: OpenIddictConstants.ConsentTypes.Implicit, displayName: "Blazor Application", secret: null, grantTypes: new List<string> { OpenIddictConstants.GrantTypes.AuthorizationCode, }, scopes: commonScopes, redirectUri: $"{blazorRootUrl}/authentication/login-callback", clientUri: blazorRootUrl, postLogoutRedirectUri: $"{blazorRootUrl}/authentication/logout-callback" ); } // Blazor Server Tiered Client var blazorServerTieredClientId = configurationSection["StudyAbp_BlazorServerTiered:ClientId"]; if (!blazorServerTieredClientId.IsNullOrWhiteSpace()) { var blazorServerTieredRootUrl = configurationSection["StudyAbp_BlazorServerTiered:RootUrl"].EnsureEndsWith('/'); await CreateApplicationAsync( name: blazorServerTieredClientId, type: OpenIddictConstants.ClientTypes.Confidential, consentType: OpenIddictConstants.ConsentTypes.Implicit, displayName: "Blazor Server Application", secret: configurationSection["StudyAbp_BlazorServerTiered:ClientSecret"] ?? "1q2w3e*", grantTypes: new List<string> //Hybrid flow { OpenIddictConstants.GrantTypes.AuthorizationCode, OpenIddictConstants.GrantTypes.Implicit }, scopes: commonScopes, redirectUri: $"{blazorServerTieredRootUrl}signin-oidc", clientUri: blazorServerTieredRootUrl, postLogoutRedirectUri: $"{blazorServerTieredRootUrl}signout-callback-oidc" ); } // Swagger Client var swaggerClientId = configurationSection["StudyAbp_Swagger:ClientId"]; if (!swaggerClientId.IsNullOrWhiteSpace()) { var swaggerRootUrl = configurationSection["StudyAbp_Swagger:RootUrl"].TrimEnd('/'); await CreateApplicationAsync( name: swaggerClientId, type: OpenIddictConstants.ClientTypes.Public, consentType: OpenIddictConstants.ConsentTypes.Implicit, displayName: "Swagger Application", secret: null, grantTypes: new List<string> { OpenIddictConstants.GrantTypes.AuthorizationCode, }, scopes: commonScopes, redirectUri: $"{swaggerRootUrl}/swagger/oauth2-redirect.html", clientUri: swaggerRootUrl ); } } private async Task CreateApplicationAsync( [NotNull] string name, [NotNull] string type, [NotNull] string consentType, string displayName, string secret, List<string> grantTypes, List<string> scopes, string clientUri = null, string redirectUri = null, string postLogoutRedirectUri = null, List<string> permissions = null) { if (!string.IsNullOrEmpty(secret) && string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) { throw new BusinessException(L["NoClientSecretCanBeSetForPublicApplications"]); } if (string.IsNullOrEmpty(secret) && string.Equals(type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase)) { throw new BusinessException(L["TheClientSecretIsRequiredForConfidentialApplications"]); } if (!string.IsNullOrEmpty(name) && await _applicationManager.FindByClientIdAsync(name) != null) { return; //throw new BusinessException(L["TheClientIdentifierIsAlreadyTakenByAnotherApplication"]); } var client = await _applicationManager.FindByClientIdAsync(name); if (client == null) { var application = new AbpApplicationDescriptor { ClientId = name, Type = type, ClientSecret = secret, ConsentType = consentType, DisplayName = displayName, ClientUri = clientUri, }; Check.NotNullOrEmpty(grantTypes, nameof(grantTypes)); Check.NotNullOrEmpty(scopes, nameof(scopes)); if (new[] { OpenIddictConstants.GrantTypes.AuthorizationCode, OpenIddictConstants.GrantTypes.Implicit }.All(grantTypes.Contains)) { application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeIdToken); if (string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) { application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeIdTokenToken); application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeToken); } } if (!redirectUri.IsNullOrWhiteSpace() || !postLogoutRedirectUri.IsNullOrWhiteSpace()) { application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Logout); } foreach (var grantType in grantTypes) { if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode) { application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode); application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.Code); } if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode || grantType == OpenIddictConstants.GrantTypes.Implicit) { application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Authorization); } if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode || grantType == OpenIddictConstants.GrantTypes.ClientCredentials || grantType == OpenIddictConstants.GrantTypes.Password || grantType == OpenIddictConstants.GrantTypes.RefreshToken || grantType == OpenIddictConstants.GrantTypes.DeviceCode) { application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Token); application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Revocation); application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Introspection); } if (grantType == OpenIddictConstants.GrantTypes.ClientCredentials) { application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.ClientCredentials); } if (grantType == OpenIddictConstants.GrantTypes.Implicit) { application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.Implicit); } if (grantType == OpenIddictConstants.GrantTypes.Password) { application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.Password); } if (grantType == OpenIddictConstants.GrantTypes.RefreshToken) { application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.RefreshToken); } if (grantType == OpenIddictConstants.GrantTypes.DeviceCode) { application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.DeviceCode); application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Device); } if (grantType == OpenIddictConstants.GrantTypes.Implicit) { application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.IdToken); if (string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) { application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.IdTokenToken); application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.Token); } } } var buildInScopes = new[] { OpenIddictConstants.Permissions.Scopes.Address, OpenIddictConstants.Permissions.Scopes.Email, OpenIddictConstants.Permissions.Scopes.Phone, OpenIddictConstants.Permissions.Scopes.Profile, OpenIddictConstants.Permissions.Scopes.Roles }; foreach (var scope in scopes) { if (buildInScopes.Contains(scope)) { application.Permissions.Add(scope); } else { application.Permissions.Add(OpenIddictConstants.Permissions.Prefixes.Scope + scope); } } if (redirectUri != null) { if (!redirectUri.IsNullOrEmpty()) { if (!Uri.TryCreate(redirectUri, UriKind.Absolute, out var uri) || !uri.IsWellFormedOriginalString()) { throw new BusinessException(L["InvalidRedirectUri", redirectUri]); } if (application.RedirectUris.All(x => x != uri)) { application.RedirectUris.Add(uri); } } } if (postLogoutRedirectUri != null) { if (!postLogoutRedirectUri.IsNullOrEmpty()) { if (!Uri.TryCreate(postLogoutRedirectUri, UriKind.Absolute, out var uri) || !uri.IsWellFormedOriginalString()) { throw new BusinessException(L["InvalidPostLogoutRedirectUri", postLogoutRedirectUri]); } if (application.PostLogoutRedirectUris.All(x => x != uri)) { application.PostLogoutRedirectUris.Add(uri); } } } if (permissions != null) { await _permissionDataSeeder.SeedAsync( ClientPermissionValueProvider.ProviderName, name, permissions, null ); } await _applicationManager.CreateAsync(application); } } }
EntityFrameworkCore
添加Nugget包:
- Volo.Abp.BackgroundJobs.EntityFrameworkCore
- Volo.Abp.Identity.EntityFrameworkCore
- Volo.Abp.OpenIddict.EntityFrameworkCore
- Volo.Abp.PermissionManagement.EntityFrameworkCore
EntityFrameworkCoreModule.cs
类添加以下依赖
- typeof(AbpIdentityEntityFrameworkCoreModule)
- typeof(AbpPermissionManagementEntityFrameworkCoreModule)
- typeof(AbpOpenIddictEntityFrameworkCoreModule)
- typeof(AbpBackgroundJobsEntityFrameworkCoreModule)
代码如下:
[DependsOn( typeof(DomainModule), typeof(AbpEntityFrameworkCoreSqlServerModule), typeof(AbpIdentityEntityFrameworkCoreModule), typeof(AbpPermissionManagementEntityFrameworkCoreModule), typeof(AbpOpenIddictEntityFrameworkCoreModule), typeof(AbpBackgroundJobsEntityFrameworkCoreModule) )] public class EntityFrameworkCoreModule:AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { ... } }
配置数据库上下文
[ReplaceDbContext(typeof(IIdentityDbContext))] [ConnectionStringName("Default")] public class MyDbContext : AbpDbContext<MyDbContext>, IIdentityDbContext { public MyDbContext(DbContextOptions<MyDbContext> options) : base(options) { } #region 身份实体 public DbSet<IdentityUser> Users { get; set; } public DbSet<IdentityUserRole> UserRoles { get; set; } public DbSet<IdentityUserClaim> UserClaims { get; set; } public DbSet<IdentityUserLogin> UserLogin { get; set; } public DbSet<IdentityUserToken> UserTokens { get; set; } public DbSet<IdentityRole> Roles { get; set; } public DbSet<IdentityRoleClaim> RoleClaims { get; set; } public DbSet<IdentityClaimType> ClaimTypes { get; set; } public DbSet<IdentitySecurityLog> SecurityLogs { get; set; } public DbSet<OrganizationUnit> OrganizationUnits { get; set; } public DbSet<IdentityLinkUser> LinkUsers { get; set; } #endregion public DbSet<PurchaseOrder> PurchaseOrders { get; set; } public DbSet<PurchaseOrderItem> PurchaseOrderItems { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.ConfigureIdentity(); modelBuilder.ConfigureOpenIddict(); modelBuilder.ConfigureBackgroundJobs(); modelBuilder.ConfigurePermissionManagement(); modelBuilder.Entity<PurchaseOrder>(p => { p.ToTable(MyDbContextConsts.DbTablePrefix + "_PurchaseOrders", MyDbContextConsts.DbSchema); p.ConfigureByConvention(); p.Property(x => x.PoNum) .IsRequired() .HasMaxLength(PurchaseConsts.MaxPoNumLength); }); modelBuilder.Entity<PurchaseOrderItem>(p => { p.ToTable(MyDbContextConsts.DbTablePrefix+"_PurchaseOrderItems",MyDbContextConsts.DbSchema); p.ConfigureByConvention(); p.Property(x=>x.ItemNum) .IsRequired() .HasMaxLength(PurchaseConsts.MaxItemNumLength); p.HasOne<PurchaseOrder>() .WithMany() .HasForeignKey(x => x.PurchaseOrderId) .IsRequired(); }); } }
添加设计时数据库上下文工厂类
public class MyDbContextFactory : IDesignTimeDbContextFactory<MyDbContext> { public MyDbContext CreateDbContext(string[] args) { var configuration = BuildConfiguration(); var builder = new DbContextOptionsBuilder<MyDbContext>() .UseSqlServer(configuration.GetConnectionString("Default")); return new MyDbContext(builder.Options); } private static IConfigurationRoot BuildConfiguration() { var builder = new ConfigurationBuilder() .SetBasePath(Path.Combine(Directory.GetCurrentDirectory(), "../DbMigrator/")) .AddJsonFile("appsettings.json", optional: false); return builder.Build(); } }
数据库迁移
public class EntityFrameworkCoreDbSchemaMigrator : IDbSchemaMigrator, ITransientDependency { private readonly IServiceProvider _serviceProvider; public EntityFrameworkCoreDbSchemaMigrator( IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public async Task MigrateAsync() { /* We intentionally resolving the MyDbContext * from IServiceProvider (instead of directly injecting it) * to properly get the connection string of the current tenant in the * current scope. */ await _serviceProvider .GetRequiredService<MyDbContext>() .Database .MigrateAsync(); } }
Application.Contracts
添加Nugget包:
- Volo.Abp.PermissionManagement.Application.Contracts
- Volo.Abp.Account.Application.Contracts
ApplicationContractsModule.cs
类添加以下依赖
- typeof(AbpAccountApplicationContractsModule)
- typeof(AbpPermissionManagementApplicationContractsModule)
添加AccountSettingNames
常量类
public class AccountSettingNames { public const string IsSelfRegistrationEnabled = "Abp.Account.IsSelfRegistrationEnabled"; public const string EnableLocalLogin = "Abp.Account.EnableLocalLogin"; }
Application
添加Nugget包:
- Volo.Abp.Account.Application
- Volo.Abp.PermissionManagement.Application
ApplicationModule.cs
类添加以下依赖
- typeof(AbpAccountApplicationModule)
- typeof(AbpPermissionManagementApplicationModule)
添加账号设置定义提供类
public class AccountSettingDefinitionProvider : SettingDefinitionProvider { public override void Define(ISettingDefinitionContext context) { // 不启用注册账号功能 context.Add( new SettingDefinition( AccountSettingNames.IsSelfRegistrationEnabled, "false", L("DisplayName:Abp.Account.IsSelfRegistrationEnabled"), L("Description:Abp.Account.IsSelfRegistrationEnabled"), isVisibleToClients: true) ); // 启用本地登录 context.Add( new SettingDefinition( AccountSettingNames.EnableLocalLogin, "true", L("DisplayName:Abp.Account.EnableLocalLogin"), L("Description:Abp.Account.EnableLocalLogin"), isVisibleToClients: true) ); } private static LocalizableString L(string name) { return LocalizableString.Create<MyLocalizationResource>(name); } }
HttpApi
添加Nugget包:
- Volo.Abp.Account.HttpApi
- Volo.Abp.PermissionManagement.HttpApi
HttpApiModule.cs
类添加以下依赖:
- typeof(AbpAccountHttpApiModule)
- typeof(AbpPermissionManagementHttpApiModule)
HttpApi.Host
添加Nugget包:
- Volo.Abp.Account.Web.OpenIddict
- Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite
HttpApiHostModule.cs
类添加以下依赖
- typeof(AbpAspNetCoreMultiTenancyModule)
- typeof(AbpAccountWebOpenIddictModule)
- typeof(AbpAspNetCoreMvcUiLeptonXLiteThemeModule)
在PreConfigureServices
方法中添加以下代码:
PreConfigure<OpenIddictBuilder>(builder => { builder.AddValidation(options => { options.AddAudiences("StudyAbp"); // 此处设置不正确会导致 application-configuration Api 无法正确获取到当前用户 currentUser 的值为空 options.UseLocalServer(); options.UseAspNetCore(); }); });
添加配置授权方法
private void ConfigureAuthentication(ServiceConfigurationContext context) { context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme); }
添加配置页面绑定方法
private void ConfigureBundles() { Configure<AbpBundlingOptions>(options => { options.StyleBundles.Configure( LeptonXLiteThemeBundles.Styles.Global, bundle => { bundle.AddFiles("/global-styles.css"); } ); }); }
修改ConfigureServices
方法
public override void ConfigureServices(ServiceConfigurationContext context) { var configuration = context.Services.GetConfiguration(); var hostingEnvironment = context.Services.GetHostingEnvironment(); ConfigureAuthentication(context); ConfigureBundles(); ConfigureUrls(configuration); ConfigureConventionalControllers(); ConfigureLocalization(); ConfigureVirtualFileSystem(context); ConfigureCors(context, configuration); ConfigureSwaggerServices(context, configuration); Configure<DynamicJavaScriptProxyOptions>(options => { options.DisableModule(PermissionManagementRemoteServiceConsts.ModuleName); }); }
修改ConfigureUrls
方法
/// <summary> /// 配置URL /// </summary> /// <param name="configuration"></param> private void ConfigureUrls(IConfiguration configuration) { Configure<AppUrlOptions>(options => { options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"]; options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"].Split(',')); options.Applications["Angular"].RootUrl = configuration["App:ClientUrl"]; options.Applications["Angular"].Urls[AccountUrlNames.PasswordReset] = "account/reset-password"; options.Applications["Vue"].RootUrl = configuration["App:ClientUrl"]; options.Applications["Vue"].Urls[AccountUrlNames.PasswordReset] = "account/reset-password"; }); }
修改 OnApplicationInitialization
方法
public override void OnApplicationInitialization(Volo.Abp.ApplicationInitializationContext context) { var app = context.GetApplicationBuilder(); var env = context.GetEnvironment(); if(env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseAbpRequestLocalization(); if (!env.IsDevelopment()) { app.UseErrorPage(); } app.UseCorrelationId(); app.UseStaticFiles(); app.UseRouting(); app.UseCors(); app.UseAuthentication(); app.UseAbpOpenIddictValidation(); if (MultiTenancyConsts.IsEnabled) { app.UseMultiTenancy(); } app.UseUnitOfWork(); app.UseAuthorization(); app.UseSwagger(); app.UseAbpSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "StudyAcme API"); var configuration = context.ServiceProvider.GetRequiredService<IConfiguration>(); c.OAuthClientId(configuration["AuthServer:SwaggerClientId"]); c.OAuthScopes("StudyAcme"); }); app.UseAuditing(); app.UseConfiguredEndpoints(); }
更改appsettings.json
配置
{ "App": { // 主机的访问地址 "SelfUrl": "https://localhost:7027", // 客户端访问地址 "ClientUrl": "http://localhost:8080", // 允许跨域访问的网站 "CorsOrigins": "https://*.StudyAcme.com,http://localhost:4200,http://localhost:8080,http://localhost:8081,http://127.0.0.1:8080", // 允许重定向的URL "RedirectAllowedUrls": "http://localhost:8080,https://localhost:7027" }, "ConnectionStrings": { "Default": "Server=.;Database=StudyAbp;Trusted_Connection=True" }, "AuthServer": { "Authority": "https://localhost:7027", "RequireHttpsMetadata": "false", "SwaggerClientId": "StudyAcme_Swagger" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异