Abp VNext微服务-从身份认证及授权开始(一)(干货,强烈推荐)
Abp-VNext是基于aspnetboilerplate的先行版本,在我看来,具有下面三个特点:
1,一个全面的技术框架:基本上囊括了.Net 技术栈上各种流行的技术应用,并且在集成度上也做得很好。
2,基于领域驱动的分层设计模型:提供了DDD分层设计的最佳实践,明确了各层的工作职责
3,模块化的开发模式
参考:aspnetboilerplate,ABP-VNext
Abp-VNext的特点决定了他在.Net 微服上能大展拳脚,下面从微服务的身份认证及授权开始,看看Abp-Vnext能帮我们做些什么。
一,基于Abp-Vnext的微服务架构目录
参考Abp-Vnext的微服务Demo,先把工作目录建立起来
先建立一个服务,用于用户身份认证及授权管理
1,建立解决方案:dotnet new sln --name Kingsun.Liujb
2,建立用于用户身份认证及授权管理的模块:
切换目录到modules,执行Abp命令:abp new Kingsun.Liujb.IDServer --ui-none -t module -csf。创建一个没有ui的模块。
3,创建模块后认识一下Abp-vnext的DDD分层架构
4,按需将模块添加到根解决方案
二,IdentityServer宿主配置
切换到microsoftservices目录,
将Modules目录中的宿主示例中的IdentityServer直接拷过来。切换到shared目录,新建一个类库:Kingusn.Liujb.Shared
注意,这里Kingusn.Liujb.Shared使用netstandard2.0
将Kingsun.Liujb.Shared添加到Kingsun.Liujb.IDServer.IdentityServer项目引用,替换原来的shared项目。
MultiTenancyConsts.cs
1
2
3
4
|
public class MultiTenancyConsts { public const bool IsEnabled = true ; } |
更改数据库链接地址为实际地址:Kingsun.Liujb.IDServer.IdentityServer项目的appsettings.json。
由于目前没有使用reids缓存,先行在IDServerIdentityServerModule中将redis相关模块注释掉。
此处要注释的地方包括:
1、IdentityServerModule的依赖注入项DependsOn中的typeof(AbpCachingStackExchangeRedisModule)
2、下面这一段
if (!hostingEnvironment.IsDevelopment()) { var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]); context.Services .AddDataProtection() .PersistKeysToStackExchangeRedis(redis, "IdServer-Protection-Keys"); }
使用”update-database“命令更新数据到数据库,运行IdentityServer项目后使用默认的用户名:admin。密码:1q2w3E* 登录
三,添加用户身份管理、租户管理模块
前面我们见识到了使用ABP-Vnext创建项目的实用性,下面将用使用ABP-VNext的模块化特性快速添加部份管理模块
先添加身份认证相关的管理模块,安装Nuget包:Volo.Abp.Identity.Web 后在IDServerIdentityServerModule.cs中添加依赖:typeof(Volo.Abp.Identity.Web.AbpIdentityWebModule)。
运行程序:
同样添加租户管理:Volo.Abp.TenantManagement.Web.AbpTenantManagementWebModule
四,添加日志查看模块
查看Abp-vnex的日志管理模块,可以看出社区版本只提供了领域层及基础设施层,没有应用服务层和表示层,也就意味着没有包含日志管理相关接口及页面:
以上依赖要在Kingsun.Liujb.IDServer.Application中使用nuget中添加
然后在IdentityServerModule的依赖注入项DependsOn中加入:
typeof(Volo.Abp.Identity.Web.AbpIdentityWebModule),
typeof(Volo.Abp.TenantManagement.Web.AbpTenantManagementWebModule),
typeof(Kingsun.Liujb.Web.IDServerWebModule)
1,新增查询日志服务
1),先建立服务约束,包括接口,数据传输对象。我这里建立了一个名为IAuditServices的接口,约束服务的使用。AuditDto以及GetAuditlogInput这两个类用于服务数据的传输。
声明如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public interface IAuditServices : IApplicationService { System.Threading.Tasks.Task<PagedResultDto<AuditDto>> GetAuditsByUsernameAsync(GetAuditlogInput input); System.Threading.Tasks.Task<PagedResultDto<AuditDto>> GetListAsync(); } public class AuditDto: EntityDto<Guid> { public string ApplicationName { get ; set ; } public string UserId { get ; set ; } public string UserName { get ; set ; } public string TenantName { get ; set ; } public DateTime ExecutionTime { get ; set ; } public int ExecutionDuration { get ; set ; } public string ClientIpAddress { get ; set ; } public string ClientName { get ; set ; } public string BrowserInfo { get ; set ; } public string HttpMethod { get ; set ; } public string Url { get ; set ; } public string Exceptions { get ; set ; } public int HttpStatusCode { get ; set ; } } public class GetAuditlogInput: PagedAndSortedResultRequestDto { public string Filter { get ; set ; } } |
2),实现服务
在Kingsun.Liujb.IDServer.Application包中实现具体的服务
[Authorize(IDServerPermissions.Audits.AuditMannage)]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
public class AuditServices : IDServerAppService, IAuditServices { private readonly IAuditLogRepository _auditLogRepository; public AuditServices(IAuditLogRepository auditLogRepository) { this ._auditLogRepository = auditLogRepository; } public async System.Threading.Tasks.Task<PagedResultDto<AuditDto>> GetAuditsByUsernameAsync(GetAuditlogInput input) { long count = await _auditLogRepository.GetCountAsync(userName: input.Filter); var list = await _auditLogRepository.GetListAsync( skipCount: input.SkipCount, maxResultCount: input.MaxResultCount, userName: input.Filter, sorting: input.Sorting ); return new PagedResultDto<AuditDto>() { TotalCount = count, Items = ObjectMapper.Map<List<AuditLog>, List<AuditDto>>(list) }; } public async Task<PagedResultDto<AuditDto>> GetListAsync() { long count = await _auditLogRepository.GetCountAsync(); var list = await _auditLogRepository.GetListAsync(); return new PagedResultDto<AuditDto>() { TotalCount = count, Items = ObjectMapper.Map<List<AuditLog>, List<AuditDto>>(list) }; } } |
暴露API
Kingsun.Liujb.IDServer.HttpApi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
[RemoteService] [Route( "api/IDServer/Audit" )] [Authorize(IDServerPermissions.Audits.AuditMannage)] public class AuditController : IDServerController, IAuditServices { private readonly IAuditServices _iDServerAuditService; public AuditController(IAuditServices _iDServerAuditService) { this ._iDServerAuditService = _iDServerAuditService; } [HttpGet] [Route( "GetAuditsByUsername" )] public async Task<PagedResultDto<AuditDto>> GetAuditsByUsernameAsync(GetAuditlogInput input) { return await _iDServerAuditService.GetAuditsByUsernameAsync(input); } [HttpGet] [Route( "GetList" )] public async Task<PagedResultDto<AuditDto>> GetListAsync() { return await _iDServerAuditService.GetListAsync(); } } |
2,新增访问权限
对服务访问进行授权管理。
在Kingsun.Liujb.IDServer.Application.Contracts包的PermissionDefinitionProvider中新建权限,并设置本地化显示名称。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
public class IDServerPermissionDefinitionProvider : PermissionDefinitionProvider { public override void Define(IPermissionDefinitionContext context) { var auditGroup = context.AddGroup(IDServerPermissions.GroupName, L( "Permission:AuditManagement" )); auditGroup.AddPermission(IDServerPermissions.Audits.AuditMannage, L( "Permission:AuditManagement" ), Volo.Abp.MultiTenancy.MultiTenancySides.Host); } private static LocalizableString L( string name) { return LocalizableString.Create<IDServerResource>(name); } } public class IDServerPermissions { public const string GroupName = "IDServer" ; public static class Audits { public const string AuditMannage = GroupName + ".Audits" ; } public static string [] GetAll() { return ReflectionHelper.GetPublicConstantsRecursively( typeof (IDServerPermissions)); } } |
权限名称本地化:Kingsun.Liujb.IDServer.Domain.Shared中的Localization目录中修改本地化Json文件
1
2
3
4
5
6
7
|
"texts" : { "ManageYourProfile" : "管理个人资料" , "SamplePageMessage" : "IDServer模块的示例页面" , "Menu:AuditManagement" : "审核日志" , "Audits" : "日志" , "Permission:AuditManagement" : "审核日志" } |
打开管理页面,可以看到新增的权限已经可以被管理
3,新增菜单
Kingsun.Liujb.IDServer.Web的Menu目录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public class IDServerMenuContributor : IMenuContributor { public async Task ConfigureMenuAsync(MenuConfigurationContext context) { if (context.Menu.Name == StandardMenus.Main) { await ConfigureMainMenu(context); } } private async Task ConfigureMainMenu(MenuConfigurationContext context) { //Add main menu items. var administrationMenu = context.Menu.GetAdministration(); var l = context.GetLocalizer<IDServerResource>(); var AuditlogsMenuItem = new ApplicationMenuItem(IDServerMenus.AuditManagementGroup, l[ "Menu:AuditManagement" ], icon: "fa fa-file-text-o" ); administrationMenu.AddItem(AuditlogsMenuItem); if (await context.IsGrantedAsync(IDServerPermissions.Audits.AuditMannage)) { AuditlogsMenuItem.AddItem( new ApplicationMenuItem(IDServerMenus.AuditManagementSelect, l[ "Audits" ], url: "~/Audit" )); } } } private const string Prefix = "IDServer" ; public const string AuditManagementGroup = "AuditManagement" ; public const string AuditManagementSelect = AuditManagementGroup + ".AuditLogs" ; |
菜单名称本地化参考权限名称本地化,修改本地化json文件即可。
打开管理页面,看到管理菜单已经添加了日志目录
4,新增日志查看页面
Index.cshtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
@page @ using Microsoft.Extensions.Localization @ using Kingsun.Liujb.IDServer.Localization @model Kingsun.Liujb.IDServer.Web.Pages.IDServer.IndexModel @inject IStringLocalizer<IDServerResource> L @section scripts{ <abp-script src= "/Pages/Audit/Index.js" /> } <abp-card> <abp-card-header> <abp-row> <abp-column size-md= "_6" > <abp-card-title>@L[ "Audits" ]</abp-card-title> </abp-column> </abp-row> </abp-card-header> <abp-card-body> <abp-table striped-rows= "true" id= "AuditsTable" ></abp-table> </abp-card-body> </abp-card> |
Index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
( function ($) { var l = abp.localization.getResource( 'IDServer' ); var dataTable = $( '#AuditsTable' ).DataTable( abp.libs.datatables.normalizeConfiguration({ serverSide: true , paging: true , searching: true , scrollX: true , order: [[1, "asc" ]], ajax: abp.libs.datatables.createAjax(kingsun.liujb.iDServer.audits.audit.getAuditsByUsername), columnDefs: [ { title:l( "UserName" ), data: "userName" }, { title: l( "ExecutionTime" ), data: "executionTime" }, { title: l( "Url" ), data: "url" }, { title: l( "HttpMethod" ), data: "httpMethod" }, { title: l( "HttpStatusCode" ), data: "httpStatusCode" } ] }) ) })(jQuery); |
此处有一个谬误,javascript调用createAjax方法时是按照AuditController中的路由[Route("api/IDServer/Audit")]应该是“ajax: abp.libs.datatables.createAjax(kingsun.liujb.iDServer.audit.getAuditsByUsername)”而非“ajax: abp.libs.datatables.createAjax(kingsun.liujb.iDServer.audits.audit.getAuditsByUsername)”,否则程序会报错。
此处另一个错误,需要在IdServerHttpApiModule上面依赖注入IdServerApplicationModule(请先引用它)
此处有另一个错误,没有配置AuditLog和AuditDto的AutoMap,需在Kingsun.Liujb.IDServer.Application的IDServerApplicationAutoMapperProfile文件中添加以下配置:
CreateMap<AuditLog, AuditDto>();
本地化:参考权限名称本地化,修改本地化json文件即可
打开页面
这只是一个比较简单的管理功能,下一篇将使用模块化的方式添加一个较为复杂的管理模块:IdentityServer管理模块。
此处有另一个错误,没有配置AuditLog和AuditDto的AutoMap,需在Kingsun.Liujb.IDServer.Application的IDServerApplicationAutoMapperProfile文件中添加以下配置:
CreateMap<AuditLog, AuditDto>();