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": "*"
}
posted @   $("#阿飞")  阅读(235)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示