【NET CORE微服务一条龙应用】第三章 认证授权与动态权限配置
介绍
在微服务的应用中,统一的认证授权是必不可少的组件,本文将介绍微服务中网关和子服务如何使用统一的权限认证
主要介绍内容为:
1、子服务如何实现和网关相同的鉴权方式
2、接口权限如何动态配置与修改
3、前后端分离模式下通用的后台管理系统(用户、权限、菜单、平台)
需提前了解知识点:
1、Jwt (JSON Web Token)
2、ClaimsPrincipal
3、Microsoft.AspNetCore.Authorization、AuthorizationPolicy、AuthorizationHandler
子服务和网关鉴权方式
首先我们需要了解一下Ocelot网关权限的使用方式,直接上代码
配置
"AuthenticationOptions": {
"AuthenticationProviderKey": "TestKey",
"AllowedScopes": ["admin","user"]
}
认证
var result = await context.HttpContext.AuthenticateAsync(context.DownstreamReRoute.AuthenticationOptions.AuthenticationProviderKey);
context.HttpContext.User = result.Principal;
if (context.HttpContext.User.Identity.IsAuthenticated)
{await _next.Invoke(context);
}
权限验证
var authorised = _scopesAuthoriser.Authorise(context.HttpContext.User, context.DownstreamReRoute.AuthenticationOptions.AllowedScopes);
从以前的代码我们可以看出认证授权的逻辑
1、HttpContext.AuthenticateAsync来进行认证验证,即验证Jwt token的有效可用性,其中AuthenticationProviderKey为身份验证提供程序标识,例如
public void ConfigureServices(IServiceCollection services)
{
var authenticationProviderKey = "TestKey";
services.AddAuthentication().AddJwtBearer(authenticationProviderKey, x => { });
}
2、当1验证通过后,我们可以通过context.HttpContext.User获取key为scope的Claim数组信息(所以token生成要带上此参数),然后与配置的AllowedScopes的数组进行交集验证,当交集大于0时即为有权限访问
所以子服务如果需要实现和网关相同的权限验证就需要实现以上的方式,用过net core默认的权限认证时会发现,权限的验证都需要体现设定好接口的可访问角色等参数,这不符合我们的需求所以我们需要实现一个自定义的权限认证AuthorizationHandler,直接上代码:
public class PermissionHandler : AuthorizationHandler<JwtAuthorizationRequirement>
{
/// <summary>
/// authentication scheme provider
/// </summary>
readonly IAuthenticationSchemeProvider _schemes;
/// <summary>
/// validate permission
/// </summary>
readonly IPermissionAuthoriser _permissionAuthoriser;
/// <summary>
/// ctor
/// </summary>
/// <param name="schemes"></param>
public PermissionHandler(IAuthenticationSchemeProvider schemes, IPermissionAuthoriser permissionAuthoriser)
{
_schemes = schemes;
_permissionAuthoriser = permissionAuthoriser;
}
/// <summary>
/// handle requirement
/// </summary>
/// <param name="context">authorization handler context</param>
/// <param name="jwtAuthorizationRequirement">jwt authorization requirement</param>
/// <returns></returns>
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, JwtAuthorizationRequirement jwtAuthorizationRequirement)
{
//convert AuthorizationHandlerContext to HttpContext
var httpContext = context.Resource.GetType().GetProperty("HttpContext").GetValue(context.Resource) as HttpContext; var defaultAuthenticate = await _schemes.GetDefaultAuthenticateSchemeAsync();
if (defaultAuthenticate != null)
{
var result = await httpContext.AuthenticateAsync(defaultAuthenticate.Name);
if (result?.Principal != null)
{
var invockResult = _permissionAuthoriser.Authorise(httpContext);
if (invockResult)
{
context.Succeed(jwtAuthorizationRequirement);
}
else
{
context.Fail();
}
}
else
{
httpContext.Response.Headers.Add("error", "authenticate fail");
context.Fail();
}
}
else
{
httpContext.Response.Headers.Add("error", "can't find authenticate");
context.Fail();
}
}
}
其中_permissionAuthoriser.Authorise为权限验证方法,继续往下看实现逻辑
public class ScopesAuthoriser : IPermissionAuthoriser
{
private readonly IPermissionRepository _permissionRepository;
private readonly IClaimsParser _claimsParser;
private readonly string _scope = "scope";
private bool _loaded = false;
public ScopesAuthoriser(IPermissionRepository permissionRepository, IClaimsParser claimsParser)
{
_permissionRepository = permissionRepository;
_claimsParser = claimsParser;
} public bool Authorise(HttpContext httpContext)
{
if (!_loaded && _permissionRepository.Permissions.Count == )
_permissionRepository.Get();
_loaded = true; var permission = _permissionRepository.Permissions
.FirstOrDefault(it => string.Equals(it.Path, httpContext.Request.Path, StringComparison.CurrentCultureIgnoreCase) && it.Method == httpContext.Request.Method); if (permission == null)
return true; var values = _claimsParser.GetValuesByClaimType(httpContext.User.Claims, _scope); var matchesScopes = permission.Scope.Intersect(values).ToList(); if (matchesScopes.Count == )
return false; return true;
}
}
其中_permissionRepository.Permissions是应用的接口列表与接口对应的可访问scope;权限仓储下面进行介绍
接口权限如何动态配置与修改
认证授权数据库设计,tb_api_resources Api资源表、tb_roles 角色表、tb_role_apis 角色Api资源关系表、tb_users 用户表、tb_user_roles 用户角色表
常规验证权限方式,是根据用户的id查询用户角色,然后验证角色是否拥有接口权限;而在网关中是反过来该接口有哪些角色可以访问;
所以我们需要初始化出应用接口对应所需角色,目前我们实现了mysql版本的权限仓储IPermissionRepository的数据查询,代码如下
public class MySqlPermissionRepository : IPermissionRepository
{
private readonly string _dbConnectionString;
private readonly string _projectName; public MySqlPermissionRepository(string dbConnectionString, string projectName)
{
_dbConnectionString = dbConnectionString;
_projectName = projectName;
}
public List<Permission> Permissions { get; private set; } = new List<Permission>();
public async Task Get()
{
using (var dbContext = new MySqlConnection(_dbConnectionString))
{
// 平台下所有需要认证Scope的接口
var apiList = await dbContext.QueryAsync<ApiInfo>(@"SELECT api.Url,api.Method,roleapi.RoleId
FROM tb_api_resources AS api
LEFT JOIN tb_role_apis AS roleapi ON api.Id = roleapi.ApiId
WHERE AllowScope = 2 AND ProjectName = @ProjectName", new { ProjectName = _projectName });
// 所有角色
var roleList = await dbContext.QueryAsync<RoleInfo>(@"SELECT Id, `Key` from tb_roles WHERE IsDel=0", new { ProjectName = _projectName });
if (apiList.Any())
{
var permission = new List<Permission>();
var apiUrlList = apiList.GroupBy(it => it.Url).Select(it => it.FirstOrDefault()).ToList();
apiUrlList.ForEach(api =>
{
var apiMethodList = apiList.Where(it => it.Url == api.Url).GroupBy(it => it.Method).Select(it => it.FirstOrDefault()).ToList();
apiMethodList.ForEach(method =>
{
var apiInfo = apiList.Where(it => it.Url == api.Url && it.Method == method.Method).FirstOrDefault();
var roleids = apiList.Where(it => it.Url == api.Url && it.Method == method.Method).Select(it => it.RoleId).ToArray();
var scopes = roleList.Where(it => roleids.Contains(it.Id)).Select(it => it.Key).ToList();
permission.Add(new Permission
{
Path = apiInfo.Url,
Method = apiInfo.Method,
Scope = scopes
});
});
});
if (permission.Count > )
Permissions = permission;
}
}
}
}
这里只会实现一次查询,如果中间有接口权限进行了修改,那么如何进行更新呢,在上一篇配置中间使用中,我们介绍了如何使用组件的定时任务和组件的监听方式,所以我们只需做对应扩展即可,定时代码就不贴了,监听代码如下:
public class BucketAuthorizeListener : IBucketListener
{
private readonly IPermissionRepository _permissionRepository; public BucketAuthorizeListener(IPermissionRepository permissionRepository)
{
_permissionRepository = permissionRepository;
} public string ListenerName => "Bucket.Authorize"; public async Task ExecuteAsync(string commandText)
{
if (!string.IsNullOrWhiteSpace(commandText) && commandText == NetworkCommandType.Reload.ToString())
await _permissionRepository.Get();
}
}
下面贴一下Bucket.Authorize如何使用代码
public void ConfigureServices(IServiceCollection services)
{
// 添加授权
services.AddApiJwtAuthorize(Configuration);
// 添加授权认证
services.AddApiJwtAuthorize(Configuration).UseAuthoriser(services, builder => { builder.UseMySqlAuthorize(); });
}
然后在需要认证授权的action或者controller加上[Authorize("permission")]属性,appsetting配置如下,也可移至配置中心
"JwtAuthorize": {
"ProjectName": "",
"Secret": "",
"Issuer": "",
"Audience": "",
"PolicyName": "",
"DefaultScheme": "",
"IsHttps": false,
"RequireExpirationTime": true,
"MySqlConnectionString": "",
"RefreshInteval":
},
前后端分离模式下通用的后台管理系统
在FamilyBucket-UI中我们可以对项目的接口权限认证方式、用户、用户角色、角色、角色权限、角色菜单等等进行配置,
同时FamilyBucket-UI还具有通用管理系统的基础模块,里面增加一个管理平台的概念,可以直接多个管理平台使用同一个用户体系
本章就不做介绍了,内容有点长,下次再做详细介绍,在多平台同时管理时项目还需要进行一些升级,截图如下
本章涉及源码均可在github中进行查看
https://github.com/q315523275/FamilyBucket
https://github.com/q315523275/FamilyBucket-UI
【NET CORE微服务一条龙应用】第三章 认证授权与动态权限配置的更多相关文章
- 【NET CORE微服务一条龙应用】第二章 配置中心使用
背景 系列目录:[NET CORE微服务一条龙应用]开始篇与目录 在分布式或者微服务系统里,通过配置文件来管理配置内容,是一件比较令人痛苦的事情,再谨慎也有湿鞋的时候,这就是在项目架构发展的过程中,配 ...
- 【NET CORE微服务一条龙应用】第一章 网关使用与配置
简介 微服务的系统应用中,网关系统使用的是ocelot,ocelot目前已经比较成熟了 ocelot就不做介绍了,等整体介绍完后再进行各类扩展介绍,ocelot源码地址:https://github. ...
- 【NET CORE微服务一条龙应用】开始篇与目录
简介 随着业务的发展和变更,项目原先的分布式框架应用业务发展已有些不适应,所以18年初开始准备使用微服务框架,当时正好看到了ocelot项目,特意翻看了源码,发现很灵活和易扩展 于是就开始了微服务的开 ...
- 【NET CORE微服务一条龙应用】应用部署
简介 系列目录:[NET CORE微服务一条龙应用]开始篇与目录 本章主要介绍https://github.com/q315523275/FamilyBucket上微服务一条龙应用,在实际使用中的应用 ...
- .Net Core微服务入门全纪录(三)——Consul-服务注册与发现(下)
前言 上一篇[.Net Core微服务入门全纪录(二)--Consul-服务注册与发现(上)]已经成功将我们的服务注册到Consul中,接下来就该客户端通过Consul去做服务发现了. 服务发现 同样 ...
- .NET Core 微服务—API网关(Ocelot) 教程 [三]
前言: 前一篇文章<.NET Core 微服务—API网关(Ocelot) 教程 [二]>已经让Ocelot和目录api(Api.Catalog).订单api(Api.Ordering)通 ...
- 基于.NET CORE微服务框架 -surging的介绍和简单示例 (开源)
一.前言 至今为止编程开发已经11个年头,从 VB6.0,ASP时代到ASP.NET再到MVC, 从中见证了.NET技术发展,从无畏无知的懵懂少年,到现在的中年大叔,从中的酸甜苦辣也只有本人自知.随着 ...
- .NET Core微服务系列基础文章索引(目录导航Final版)
一.为啥要总结和收集这个系列? 今年从原来的Team里面被抽出来加入了新的Team,开始做Java微服务的开发工作,接触了Spring Boot, Spring Cloud等技术栈,对微服务这种架构有 ...
- .NET Core微服务之基于Consul实现服务治理
Tip: 此篇已加入.NET Core微服务基础系列文章索引 一.Consul基础介绍 Consul是HashiCorp公司推出的开源工具,用于实现分布式系统的服务发现与配置.与其他分布式服务注册与发 ...
随机推荐
- rem 是如何实现自适应布局的
摘要:rem是相对于根元素<html>,这样就意味着,我们只需要在根元素确定一个px字号,则可以来算出元素的宽高.本文讲的是如何使用rem实现自适应.· rem这是个低调的css单位,近一 ...
- java开发师笔试面试每日12题(3)
1.JDK和JRE的区别是什么? Java运行时环境(JRE)是将要执行Java程序的Java虚拟机.它同时也包含了执行applet需要的浏览器插件.Java开发工具包(JDK)是完整的Java软件开 ...
- android踩坑日记1
Android四大组件-活动.服务.广播.碎片 情况一 应用场景:定时从服务器获取数据,然后活动或者碎片中根据最新获得的数据,更新UI. 思考: 首先定时,想到定时器,推荐使用系统自带的AlertMa ...
- Exp5 MSF基础运用 20154320 李超
实验后回答问题 用自己的话解释什么是exploit,payload,encode. exploit:起运输的作用,将数据传输到对方主机. payload:其实就是指装载的“具体内容”.就相当于shel ...
- 深入Java集合学习系列:LinkedHashMap的实现原理
参考下面链接: http://zhangshixi.iteye.com/blog/673789
- zookeeper原理与安装
Zookeeper是一个开源的分布式的,为分布式应用提供协调服务的Apache项目. 1. Zookerper工作机制 2. Zookeeper工作特点 3. Zookeeper文件系统: ...
- Django关联数据库时报错TypeError: __init__() missing 1 required positional argument: 'on_delete'
sgrade = models.ForeignKey("Grades",) 执行python manage.py makemigrations后出现TypeError: __ini ...
- nohup和&后台运行,进程查看及终止
1.nohup 用途:不挂断地运行命令. 语法:nohup Command [ Arg … ] [ & ] 无论是否将 nohup 命令的输出重定向到终端,输出都将附加到当前目录的 nohup ...
- Android插件化的兼容性(上):Android O的适配
首先声明,<Android插件化开发指南>这本书所介绍的Android底层是基于Android6.0(API level 23)的,而本书介绍的各种插件化解决方案,以及配套的70多个例子, ...
- 打开Python IDLE时的错误:Subprocess Startup Error
比较常见的是这个 方法1: 修改[Python目录]\Lib\idlelib\PyShell.py文件,在1300行附近,将def main():函数下面 use_subprocess = True ...