企业级工作流解决方案(十二)--集成Abp和ng-alain--用户身份认证与权限验证
多租户
如果系统需要支持多租户,那么最好事先定义好多租户的存储部署方式,Abp提供了几种方式,根据需要选择,每一个用户身份认证与权限验证都需要完全的隔离
这里设计的权限数据全部存储在缓存中,每个租户单独建立缓存Key,见权限系统服务章节介绍。
用户accesstoken
accesstoken的定义就不多的介绍了,Abp其实就是直接使用微软IdentityModel这套组件,并且zero项目还直接使用的微软的用户角色相关管理,这里面性能存在一定的问题,而且使用相对比较复杂,还不利于扩展,其实功能就那么一些,完全没有必要用这一套用户角色管理。
其实这里要做的核心扩展,就是自定义Claim信息,accesstoken信息其实是存在Claim集合里面的,我们可以自定义Claim信息,用户登录的时候,调用组件提供的接口,创建用户访问的accesstoken信息,我这里扩展了RoleIds集合信息和UserName信息,UserName在系统很多地方都可能用到,而RoleIds集合信息用于做权限控制。
实现代码
public class AuthTokenProvider: IAuthTokenProvider
{
private readonly TokenAuthConfiguration _configuration;
public AuthTokenProvider(TokenAuthConfiguration configuration)
{
_configuration = configuration;
}
public AuthenticateResultModel Authenticate(LoginResultModel loginResultModel)
{
List<Claim> claims = new List<Claim>();
claims.Add(new Claim(AbpClaimTypes.UserId, loginResultModel.UserId.ToString()));
if (loginResultModel.TenantId.HasValue)
{
claims.Add(new Claim(AbpClaimTypes.TenantId, loginResultModel.TenantId.ToString()));
}
claims.Add(new Claim(AbpClaimTypes.RoleIds, loginResultModel.RoleIds)); // 自定义RoleIds和UserName申明
claims.Add(new Claim(AbpClaimTypes.UserName, loginResultModel.UserName)); var accessToken = CreateAccessToken(claims); return new AuthenticateResultModel
{
AccessToken = accessToken,
EncryptedAccessToken = GetEncrpyedAccessToken(accessToken),
ExpireInSeconds = (int)_configuration.Expiration.TotalSeconds,
UserId = loginResultModel.UserId
};
} private string CreateAccessToken(IEnumerable<Claim> claims, TimeSpan? expiration = null)
{
var now = DateTime.UtcNow; var jwtSecurityToken = new JwtSecurityToken(
issuer: _configuration.Issuer,
audience: _configuration.Audience,
claims: claims,
notBefore: now,
expires: now.Add(expiration ?? _configuration.Expiration),
signingCredentials: _configuration.SigningCredentials
); return new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
} private static List<Claim> CreateJwtClaims(List<Claim> claims)
{
var nameIdClaim = claims.First(c => c.Type == ClaimTypes.NameIdentifier); // Specifically add the jti (random nonce), iat (issued timestamp), and sub (subject/user) claims.
claims.AddRange(new[]
{
//new Claim(JwtRegisteredClaimNames.Sub, nameIdClaim.Value),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.Now.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)
}); return claims;
} private string GetEncrpyedAccessToken(string accessToken)
{
return SimpleStringCipher.Instance.Encrypt(accessToken, CoreModule.DefaultPassPhrase);
}
}
Ng-alain前端页面Token
ng-alain前端页面也有权限管理,主要是delon项目里面的权限管理,核心其实也是在Token的管理,ITokenService,定义了管理Token的接口,ITokenModel定义了token和一个自定义存储的数据字典,在路由拦截的时候,添加了对Token是否为空的验证,我们在登录的时候,对token进行赋值,退出登录清空token。
默认Token是存储在LocalStorage里面的,根据需要,可以改为SessionStorage。
用户登录
/**
* 登录结果回调
* @param authenticateResult 登录结果
*/
private processAuthenticateResult(authenticateResult: AuthenticateResultModel) {
if (authenticateResult.accessToken) {
// 设置用户Token信息
this.tokenService.set({
token: authenticateResult.accessToken, userId: authenticateResult.userId
, expireInSeconds: authenticateResult.expireInSeconds,encryptedAccessToken:authenticateResult.encryptedAccessToken
});
// 重新获取 StartupService 内容,我们始终认为应用信息一般都会受当前用户授权范围而影响
this.startupSrv.load().then(() => {
let url = this.tokenService.referrer.url || '/';
if (url.includes('/passport')) url = '/';
this.router.navigateByUrl(url);
}); } else {
this.error = "用户名或密码错误!";
return;
}
}
退出登录
logout() {
this.tokenService.clear();
this.signalRService.CloseSignalr();
this.router.navigateByUrl(this.tokenService.login_url);
}
ng-alain权限管理
export interface ITokenModel {
[key: string]: any;
token: string;
}
export interface AuthReferrer {
url?: string;
}
export interface ITokenService {
set(data: ITokenModel): boolean;
/**
* 获取Token,形式包括:
* - `get()` 获取 Simple Token
* - `get<JWTTokenModel>(JWTTokenModel)` 获取 JWT Token
*/
get(type?: any): ITokenModel;
/**
* 获取Token,形式包括:
* - `get()` 获取 Simple Token
* - `get<JWTTokenModel>(JWTTokenModel)` 获取 JWT Token
*/
get<T extends ITokenModel>(type?: any): T;
clear(): void;
change(): Observable<ITokenModel>;
/** 获取登录地址 */
readonly login_url: string;
/** 获取授权失败前路由信息 */
readonly referrer?: AuthReferrer;
}
http拦截
由于Abp后端需要验证accesstoken,abp对错误信息和请求结果信息进行了封装,因此,需要替换ng-alain的http拦截器,自定义拦截器。
自定义拦截器主要是对每一个http请求,从ITokenService里面获取token,添加到请求Header里面;以及对答复内容,提取答复内容和显示错误信息。
添加请求Token
protected addAuthorizationHeaders(headers: HttpHeaders): HttpHeaders {
let authorizationHeaders = headers ? headers.getAll('Authorization') : null;
if (!authorizationHeaders) {
authorizationHeaders = [];
}
if (!this.itemExists(authorizationHeaders, (item: string) => item.indexOf('Bearer ') == 0)) {
let token = (this.injector.get(DA_SERVICE_TOKEN) as ITokenService).get();
// let token = this._tokenService.getToken();
if (headers && token) {
headers = headers.set('Authorization', 'Bearer ' + token.token);
}
}
return headers;
}
答复内容参照abp提供的前端框架代码即可
Ng-alain前端权限验证
也是delon项目里面的acl功能模块,只需要在用户登录成功的时候,把用户能够访问的操作Code集合传递给acl,以及在页面使用的地方,定义权限控制的Code即可
// ACL:设置权限为全量
// this.aclService.setFull(true);
this.aclService.setAbility(res.operate);
控件控制
<button (click)="add()" acl [acl-ability]="'adminuser-add'" nz-button nzType="primary">新建</button>
列表按钮控制
{
text: '删除', type: 'del', acl:{ ability:['adminuser-delete']}, click: (item: any) => {
this.http.delete(`api/services/app/SEC_AdminUser/Delete?Id=${item.id}`).subscribe(() => this.st.reload());
}
},
路由控制
{ path: 'operate', loadChildren: './sec-operate/sec-operate.module#SecOperateModule', canActivate: [ACLGuard], data: {
guard: <ACLType>{ability: ['operate-mgt']}
}}, // 操作管理
企业级工作流解决方案(十二)--集成Abp和ng-alain--用户身份认证与权限验证的更多相关文章
- 企业级工作流解决方案(六)--微服务消息处理模型之与Abp集成
身份认证传递 对于Abp比较熟悉的朋友应该对他里面的用户身份认证比较熟悉,他是通过实现微软提供的权限认证方式实现的,用户登录身份信息存储在System.Security.Claims.ClaimsPr ...
- JEECG 集成KiSSO单点登录实现统一身份认证
JEECG 集成KiSSO单点登录实现统一身份认证 JEECG 如何为其他第三方系统实现统一身份认证服务,实现单点登录? 第三方系统如何对接呢? 今天为大家揭开这层面纱,让大家了解实质,使用它更快速的 ...
- 企业级工作流解决方案(十五)--集成Abp和ng-alain--Abp其他改造
配置功能增强 Abp定义了各种配置接口,但是没有定义这些配置数据从哪里来,但是管理配置数据对于一个应用程序来说,是必不可少的一件事情. .net的配置数据管理,一般放在Web.config文件或者Ap ...
- 企业级工作流解决方案(十)--集成Abp和ng-alain--权限系统
权限系统 应用系统离不开权限控制,权限中心不一定能抽象出所有的业务场景,这里定义的权限系统不一定能够满足所有的场景,但应该可以满足多数的业务需求. Abp的zero项目也定义了权限相关的表,但里面很多 ...
- 企业级工作流解决方案(十一)--集成Abp和ng-alain--权限系统服务
权限系统主要定义为管理员增删改查权限数据,直接读取数据库,权限系统服务主要定义为供其他系统调用的权限验证接口,定义为两个不同的微服务. 权限系统有一个特点,数据变动比较小,数据量本身并不是很大,访问量 ...
- 企业级工作流解决方案(十四)--集成Abp和ng-alain--自动化脚本
对于.net方向,做过自动化的,应该没有人不熟悉msbuild吧,非常强大的代码编译工具,.net平台的编译工作都是交给他来完成的,包括.net core的命令,本质上都是调用msbuild来执行的 ...
- 企业级工作流解决方案(十三)--集成Abp和ng-alain--数据库读写分离
说到程序里面数据库管理,无非就是两件事情,一是数据库操作,对于数据库的操作,各种程序语言都有封装,也就是所谓的ORM框架,.net 方向一般用得比较多和就是.net framework和dapper, ...
- ABP源码分析三十二:ABP.SignalR
Realtime Realtime是ABP底层模块提供的功能,用于管理在线用户.它是使用SignalR实现给在线用户发送通知的功能的前提 IOnlineClient/OnlineClient: 封装在 ...
- SpringCloud微服务实战——搭建企业级开发框架(十二):OpenFeign+Ribbon实现负载均衡
Ribbon是Netflix下的负载均衡项目,它主要实现中间层应用程序的负载均衡.为Ribbon配置服务提供者地址列表后,Ribbon就会基于某种负载均衡算法,自动帮助服务调用者去请求.Ribbo ...
随机推荐
- HashMap的理解
Hashmap的实现原理 默认它是存放了16个链表头的数组,存储数据的时候key先生成hashcode,根据hashcode把数据存放到相应链表中,那么是如何确定存放到哪个链表中的呢?采用hashco ...
- SQL DELETE语句如何让表使用别名的方法
DELETE 别名 FROM 表名称 别名 WHERE 列名称 = 值
- 51nod 最大M子段和系列(1052、1053、1115)
51nod1052 数据量小,可使用O(N*M)的DPAC,递推公式: dp[i][j]=max(dp[i-1][j-1], dp[i][j-1])+a[j]; dp[i][j]表示前j个数取 i 段 ...
- poj1655 Balancing Act (dp? dfs?)
Balancing Act Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 14247 Accepted: 6026 De ...
- CSS中-moz、-ms、-webkit、-o的意思
-moz代表firefox浏览器私有属性 -ms代表ie浏览器私有属性 -webkit代表safari.chrome浏览器私有属性 -o代表opera浏览器私有属性 上述这些是为了兼容老版本的写法:
- 平时查询linux服务器与应用版本
###JDK版本###java -version ##Tomcat环境版本###进入到tomcat目录的bin下./version.sh ###服务器###(1)osuname -a 查看releas ...
- 痞子衡嵌入式:基于恩智浦i.MXRT1010的MP3音乐播放器(RT-Mp3Player)设计
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是基于i.MXRT1011的MP3播放器参考设计. i.MXRT1011是恩智浦i.MXRT四位数系列的入门型号,虽然是入门级,可也是50 ...
- PHP定界符的用法
当面临大段的负责的文本输入的时候,建议使用定界符. 例如: $age = 28; $str = <<<cont 第一行数据 第二行数据 特殊字符 ' " asdf abc ...
- 获取url中查询字符串参数
// 获取url中查询字符串参数 例如http://www.test.com?a=1&b=2 function RequestParamete() { var url = window.loc ...
- OpenCascade拓扑对象之:有向边和共边
@font-face { font-family: "Times New Roman" } @font-face { font-family: "宋体" } @ ...