abp中多种登陆用户的设计
项目地址:https://gitee.com/bxjg1987/abp
场景
在《学校管理系统》中,学生、家长、教师、教务都可能登陆,做一些属于他们自己的操作。这些用户需要的属性各不相同,比如学生有学号,而教师没有。
应用程序用户
在编码时,经常需要获取当前登陆用户的信息,这个当前登陆用户就是应用程序用户。asp.net提供了一整套方案来实现应用程序用户,包括身份验证、授权、asp.net identity等
应用程序用户与业务场景中的用户不同,应用程序用户只需要区别是谁,最简单情况只需要知道用户id,它不关心这个用户具体是教师还是学生或其它类型的用户。基于这个用户id还可以实现角色、授权等操作。 浅显点理解应用程序用户主要是识别用户id,方便实现角色和授权
而“学生”、“教师”则是《学校管理系统》这个场景中的具体业务概念。
abp中有个abp zero模块,它已实现应用程序用户管理、登陆、角色、授权等功能。
与abp用户一对一关联
当业务用户需要登陆时,一对一关联到应用程序用户。*
这样系统本身提供的登陆、注销、角色、授权等功能几乎保持不变,按需要可以实现多种业务用户类型。
模块化
我们的系统是按业务分模块开发的,参考:https://www.bilibili.com/video/BV1b5411L7Hf/
有些业务模块可能存在业务用户,比如【商城模块】中的“顾客”,在设计时需要考虑模块化和可扩展性。
由于是独立模块,所以我们不知道将来模块被什么系统引用,因此也不知道具体的AbpUser类型,因为那是模块使用方自己定义的,因此在开发业务模块时不应该出现具体的AbpUser的类型,需要关联时只能关联Id,必要时可以引用抽象的AbpUser及其管理类。
Core层
按ddd和abp的方式这层需要建立:聚合根、实体、值对象、领域服务、领域事件、仓储接口。
下面以《学校管理系统》场景说明
实体
按ddd方式定义学生实体(它应该是聚合根)。
定义学号、所属学院、所属专业等属性,重点是它有个UserId属性,关联到应用程序用户,注意不要使用导航属性关联到AbpUser,一来ddd建议一个聚合中的实体不要使用导航属性关联到另一个聚合中的实体,二来我们使用的是模块化开发方式,我们在开发自己的模块时并不会知道模块将来被谁使用,因此就不会知道具体的类型,因为在abp中,用户是由开发人员自定义的。
不要考虑模块使用方使用继承来扩展实体,建议使用abp提供的IExtensionObject接口或动态属性系统。
领域实体中可以触发事件,以便模块调用方扩展
领域服务、领域事件、仓储接口。
这个根据需要决定是否定义。
领域服务可以提供虚方法以便模块使用方法提供子类重写,也可以触发相应事件让模块使用方去订阅。
由于将来可能增加更多用户类型,领域服务可以考虑抽象封装在BXJG.Utils(依赖abp的通用功能模块)中
EFCore层
ef映射和种子数据的处理
Application层
新增业务用户时考虑同时建立并关联用户、删除时则一并删除。
可以提供虚方法,以便模块调用方继承并重写
在模块中,没有将新增、删除应用程序用户的逻辑预留给模块使用方,而是在模块内部直接做了,模块调用方可以订阅UserCreating、UserDeleting事件来插入自己的逻辑
由于将来可能增加更多用户类型,应用服务可以考虑抽象封装在BXJG.Utils.Application中
session与登陆
如《学生管理系统》有个学生后台,里面全都是学生可以操作的功能,做这些功能时通常需要获取当前登陆学生的id。abp的seession功能只能获取当前登陆用户id,这是abpUser的id,这个id并不是我们需要的,它与学生实体是一对一关联的。
我们可以通过abp的seesion获取当前abpUser的id,然后去对应用户表查询得到业务用户的id,但这样比较浪费性能。
我们的思路是在用户登陆时将关联的业务场景用户的id存储到claim中,然后提供一个类似abpsession的session来在需要是提供当前业务用户的id
我们可以为每种用户建立登陆页面,在登陆时存储业务用户id到claim中,也可以在统一的登陆页面加各判断,然后获取对应业务用户类型表中的业务用户id
session的设计也可以抽象出来,因为有多种用户类型
如何使用
abp官方文档教程中有[扩展session](https://aspnetboilerplate.com/Pages/Documents/Articles%5CHow-To%5Cadd-custom-session-field-aspnet-core)的说明,我们也是按这个思路做的。由于系统可能存在多种业务用户,因此需要简单封装下,下面看看商城模块中的顾客是如何实现session和登陆的
实体实现IBusinessUserEntity
1 public class CustomerEntity : FullAuditedAggregateRoot<long>, IBusinessUserEntity , IMustHaveTenant, IExtendableObject
2 //略....
定义session接口和实现
1 public interface ICustomerSession : IBusinessUserSession<long>{}
2
3 public class CustomerClaimSession : BusinessUserClaimSession<long>, ICustomerSession
4 {
5 public CustomerClaimSession(IPrincipalAccessor principalAccessor,
6 IMultiTenancyConfig multiTenancy,
7 ITenantResolver tenantResolver,
8 IAmbientScopeProvider<SessionOverride> sessionOverrideScopeProvider) : base(principalAccessor, multiTenancy, tenantResolver, sessionOverrideScopeProvider, CoreConsts.CustomerIdClaim)
9 {
10 }
11 }
在模块中实现ioc注入
1 public override void Initialize()
2 {
3 IocManager.IocContainer.Register(Component.For<ICustomerSession>()
4 .ImplementedBy<CustomerClaimSession>()
5 .LifestyleCustom<MsScopedLifestyleManager>()
6 .Named("sdf234sdf"));//CustomerClaimSession的父类已单例注册了,需要重命名下
7
8 }
定义登陆器接口和实现
public interface ICustomerLoginManager<TUser> : IBusinessUserLoginManager<TUser> { }
/// <summary>
/// 提供与顾客登陆相关功能
/// </summary>
public class CustomerLoginManager<TTenant,
TRole,
TUser,
TUserManager> : BusinessUserLoginManager<CustomerEntity,
long,
TTenant,
TRole,
TUser,
TUserManager>, ICustomerLoginManager<TUser>
where TTenant : AbpTenant<TUser>
where TRole : AbpRole<TUser>, new()
where TUser : AbpUser<TUser>
where TUserManager : AbpUserManager<TRole, TUser>
{
public CustomerLoginManager(IRepository<CustomerEntity, long> repository,
TUserManager userManager) : base(repository,
userManager,
CoreConsts.CustomerRoleName,
CoreConsts.CustomerIdClaim)
{ }
}
主程序的XXX.Core的Module类中添加依赖注入
1 IocManager.Register<ICustomerLoginManager<User>, CustomerLoginManager<Tenant, Role, User, UserManager>>(Abp.Dependency.DependencyLifeStyle.Transient);
使用登陆器
最后在主程序的UserClaimsPrincipalFactory中
1 public class UserClaimsPrincipalFactory : AbpUserClaimsPrincipalFactory<User, Role>
2 {
3 private readonly ICustomerLoginManager<User> customerLoginManager;
4 public UserClaimsPrincipalFactory(
5 UserManager userManager,
6 RoleManager roleManager,
7 IOptions<IdentityOptions> optionsAccessor, ICustomerLoginManager<User> customerLoginManager)
8 : base(
9 userManager,
10 roleManager,
11 optionsAccessor)
12 {
13 this.customerLoginManager = customerLoginManager;
14 }
15 public override async Task<ClaimsPrincipal> CreateAsync(User user)
16 {
17 var claim = await base.CreateAsync(user);
18 var c = await customerLoginManager.GetBusinessUserClaim(user);
19 if(c!=null)
20 claim.Identities.First().AddClaim(c);
21 return claim;
22 }
23 }
流程说明
登陆时AbpLoginManager会调用UserClaimsPrincipalFactory来向当前登陆用户的Claims中插入Claim
UserClaimsPrincipalFactory会调用ICustomerLoginManager,先判断用户是否是顾客的角色,若是则根据用户Id找到顾客Id,然后将顾客Id存储到Claims中
后续我们在需要获取当前登陆的顾客的id时在我们的服务中依赖注入ICustomerSession就可以了,它会从当前登陆用户的Claims中去找到顾客id
abp中多种登陆用户的设计的更多相关文章
- JavaWeb-SpringSecurity在数据库中查询登陆用户
系列博文 项目已上传至guthub 传送门 JavaWeb-SpringSecurity初认识 传送门 JavaWeb-SpringSecurity在数据库中查询登陆用户 传送门 JavaWeb-Sp ...
- 在ABP VNext框架中处理和用户相关的多对多的关系
前面介绍了一些ABP VNext架构上的内容,随着内容的细化,我们会发现ABP VNext框架中的Entity Framework处理表之间的引用关系还是比较麻烦的,一不小心就容易出错了,本篇随笔介绍 ...
- C#开发微信门户及应用(10)--在管理系统中同步微信用户分组信息
在前面几篇文章中,逐步从原有微信的API封装的基础上过渡到微信应用平台管理系统里面,逐步介绍管理系统中的微信数据的界面设计,以及相关的处理操作过程的逻辑和代码,希望从更高一个层次,向大家介绍微信的应用 ...
- [2018-12-18]ABP中的AsyncCrudAppService介绍
前言 自从写完上次略长的<用ABP入门DDD>后,针对ABP框架的项目模板初始化,我写了个命令行工具Abp-CLI,其中子命令abplus init可以从github拉取项目模板以初始化项 ...
- Java生鲜电商平台-电商中海量搜索ElasticSearch架构设计实战与源码解析
Java生鲜电商平台-电商中海量搜索ElasticSearch架构设计实战与源码解析 生鲜电商搜索引擎的特点 众所周知,标准的搜索引擎主要分成三个大的部分,第一步是爬虫系统,第二步是数据分析,第三步才 ...
- 基于 Egg.js 框架的 Node.js 服务构建之用户管理设计
前言 近来公司需要构建一套 EMM(Enterprise Mobility Management)的管理平台,就这种面向企业的应用管理本身需要考虑的需求是十分复杂的,技术层面管理端和服务端构建是架构核 ...
- ABP源码分析四十七:ABP中的异常处理
ABP 中异常处理的思路是很清晰的.一共五种类型的异常类. AbpInitializationException用于封装ABP初始化过程中出现的异常,只要抛出AbpInitializationExce ...
- Apple、Google、Microsoft的用户体验设计原则
轻巧的Apple 注重设计过程: 在设计过程中引入用户交互的5个目标: 了解您的目标客户 分析用户的工作流 构造原型系统 观察用户测试 制定观察用户准则 做出设计决定 避免功能泛滥 80% 方案 优秀 ...
- ABP中使用OAuth2(Resource Owner Password Credentials Grant模式)
ABP目前的认证方式有两种,一种是基于Cookie的登录认证,一种是基于token的登录认证.使用Cookie的认证方式一般在PC端用得比较多,使用token的认证方式一般在移动端用得比较多.ABP自 ...
随机推荐
- 记angular和asp.net使用grpc进行通信
AspNetCore配置grpc服务端 新建一个Demo项目: GrpcStartup, 目录结构如下图: GrpcStartup.GrpcServices需要安装下面的依赖 <PackageR ...
- CF-125E MST Company (单度限制最小生成树)
参考红宝书 题目链接 对除 1 号点顶点外的点集,求一次最小生成森林,对于最小生成森林的联通分量,选择最短的一条边与 1 号点相连.设此时 1 号点的度为 \(k_0\),如果 \(k_0\lt L\ ...
- zjnu1189 土地租用(完整版)
Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 77 Accepted: 10 Description 随着YYHS的OI ...
- Codeforces ECR86 C. Yet Another Counting Problem(规律,区间)
题意:给你两个正整数a和b,询问q次,每次给你一个区间[l,r],问[l,r]中有多少数字满足:x%a%b!=a%b%a. 题解:看公式无从下手的题,一般都是要找规律的.首先,我们知道,假如x%a%b ...
- 恢复win10 LTSC 2019 图片查看器功能
1.开始–运行–输入"regedit"打开注册表. 2. 在打开的注册表编辑器中,从左侧依次展开:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Win ...
- CF1466-C. Canine poetry
CF1466-C. Canine poetry 题意: 给出一个字符串,这个字符串里面可能会包含多个回文子字符串.现在你可以任意修改这个字符串中的任意一个字符任意次数,问你最少多少操作数之后这个字符串 ...
- OpenStack Train版-11.安装horizon服务(计算节点)
OpenStack仪表板Dashboard服务的项目名称是Horizon,它所需的唯一服务是身份服务keystone,开发语言是python的web框架Django. 安装Train版本的Horizo ...
- 由CloudStack项目引起的ESXI嵌套虚拟化引起的二级虚拟机无法被访问
关于这个问题,主要以文字描述为主,最终解决方法其实就一个步骤. 问题描述: 某客户需要部署某企业的云平台,但是由于年前没有足够的物理机资源,所以提供的资源均为虚拟机,现在让我们做技术评估. 其实观察整 ...
- IOS键盘收起后,页面底部留白处理
环境:vue+vant 的H5页面 场景:输入框输入信息时,如登录.注册等表单信息 问题:键盘收回后页面底部留白,导致dialog组件按钮位移,视觉上,其中的按钮无法正常工作 解决方案:监听失去焦点时 ...
- Pycharm缺少环境变量+无法获取libcudnn.so.6
在终端输入: echo LD_LIBRARY_PATH, 并将其内容 添加至Pycharm的 run -> Edit configuration -> Environment variab ...