针对不同用户角色的基于权限的授权
Permission based Authorization for different user roles
我正在 Dot Net Core 3.1 中开发 WebAPI 微服务项目。我在 Azure AD 中对用户进行身份验证。我从 Azure AD 收到用户的 jwt 令牌。这个 Jwt 令牌以及其他东西也包含这样的角色:
"roles": [
"Engineer"
],
我有一个 数据库,其中包含与其关联的角色和权限列表。根据与角色关联的权限,用户可以访问 Put/Patch/Get API。比如说,管理员可以访问 PUT 和补丁 API,而普通用户(角色 - 工程师)只能访问 Get API.
如何根据用户的角色和他拥有的权限来授权他。任何建议、想法、代码示例或参考文章都会很有帮助。
谢谢...
编辑:我不想要确切的答案,只是关于我应该如何解决这个问题的要点或指导。我在 Internet 上寻找解决方案 2 3 天,但找不到任何有意义的东西,那就是为什么我在这里问。
我会提供更多细节:我已经提到了几个链接:
Permission based Authorization
此处作者按角色对声明进行分组。他正在使用 Identity Framework 创建角色并添加声明。但我不能使用它,因为我正在使用 AD 登录 jwt 令牌。此外,为了从 jwt 令牌中获取我的角色,我需要 HTTPContext,我无法在任何 class 中访问它,截至目前,它只能在 API Controller class 中访问,在那里我可以使用以下代码获取角色:
User.Identities.SelectMany(s => s.Claims).Where(s => s.Type.Contains("role")).Select(s => s.Value).FirstOrDefault()
因此,一旦我掌握了角色,我就会对我的数据库服务进行 httpclient get 调用,以获取与之相关的所有角色和权限。但是,我该如何动态授权我的 API 控制器 Rest 调用?
我想出了一些解决难题的方法。一种方法是通过 Custom Authorization Policy Providers using IAuthorizationPolicyProvider
在上述方法中,我会创建自定义策略属性,例如
[MinimumAccessAuthorize("Read","ModuleName1")]
[MinimumAccessAuthorize("Write","ModuleName2")]
在我的 Web API 操作方法之上,IAuthorizationPolicyProvider 会处理其余部分。这种方法的唯一问题是我需要硬编码属性以供 Read/Write 访问,此后每个控制器中的每个 god dam 操作方法都可用于模块。
解决这个问题的另一种方法是提出一个自定义中间件,在请求到达控制器之前对用户进行身份验证。
所以我创建了一个自定义中间件:Resource for custom middleware
在我的中间件中,我有一个名为 invokeAsync
的函数
public async Task InvokeAsync(HttpContext context, [FromServices] IRoleService roleService, [FromServices] IPermissionService permissionService)
{
var roles = context.User.Identities.SelectMany(s => s.Claims).Where(s => s.Type.Contains("role")).Select(s => s.Value).FirstOrDefault();
if (roles == null)
{
context.Response.StatusCode = 401;
return;
}
var requestType = context.Request.Method;
var controller = context.Request.Path.Value.Replace("/api/v1/", "").Split('/')[0];
PermissionType typeRequired = requestType == "GET" ? PermissionType.Read : PermissionType.Write;
var (IsSuccessRole, roleResult) = await roleService.GetRoleAsync();
if (!IsSuccessRole)
{
context.Response.StatusCode = 500;
return;
}
int _roleId = roleResult.Where(r => r.RoleName == roles).Select(r => r.Id).FirstOrDefault();
var (IsSuccess, permissionResult) = await permissionService.GetPermissionAsync(_roleId);
if (!IsSuccess)
{
context.Response.StatusCode = 500;
return;
}
var validRoles = from p in permissionResult
join k in GetControllerModuleMaps()
on p.ProjectModule.ModuleName equals k.ModuleName
where k.ControllerName == controller && p.PermissionType >= typeRequired
select p.RoleId;
if (!validRoles.Any())
{
context.Response.StatusCode = 401;
return;
}
await next(context);
}
我正在传递 HTTP 上下文和 2 个 HTTP 客户端服务以从数据库中获取权限和角色数据。
我现在通过上下文和基本的 linq 查询检查 jwt 令牌中存在的用户角色。
然后进行基本验证以查看角色是否不为空。
我检查请求类型,然后查看它是 GET、PUT 还是 Patch 请求。根据请求,我将值存储为 PermissionType 中的枚举。
我将 REST 请求命中的控制器详细信息存储在控制器变量中。
现在,我使用角色服务在数据库中查询与我当前的 jwt 令牌角色关联的 roleId。然后,我使用权限服务从数据库中获取与该 roleID 关联的权限。
现在基于从数据库成功查询权限,我编写了一个 Linq 查询语法,其中我将来自数据库的权限结果数据与包含控制器名称和模块名称的 C# 记录列表连接起来值(不是密钥对值)。
示例记录列表如下所示:
var toRet = new List<ControllerRecord>();
toRet.Add(new ControllerRecord("Controller1", "Module 1"));
toRet.Add(new ControllerRecord("Controller1", "Module 2"));
我检查我的控制器记录是否与请求控制器路径匹配,并且与数据库中该角色关联的权限大于 Rest API 中请求的权限(这有效,因为我有枚举 NoAccess = 0 < GET request = Only Read = 1 < Put/Patch/Post request = Write = 2 )
如果有任何有效的角色,我让流程照常继续。
我就是这样把它拉过来的...
我正在 Dot Net Core 3.1 中开发 WebAPI 微服务项目。我在 Azure AD 中对用户进行身份验证。我从 Azure AD 收到用户的 jwt 令牌。这个 Jwt 令牌以及其他东西也包含这样的角色:
"roles": [
"Engineer"
],
我有一个 数据库,其中包含与其关联的角色和权限列表。根据与角色关联的权限,用户可以访问 Put/Patch/Get API。比如说,管理员可以访问 PUT 和补丁 API,而普通用户(角色 - 工程师)只能访问 Get API.
如何根据用户的角色和他拥有的权限来授权他。任何建议、想法、代码示例或参考文章都会很有帮助。
谢谢...
编辑:我不想要确切的答案,只是关于我应该如何解决这个问题的要点或指导。我在 Internet 上寻找解决方案 2 3 天,但找不到任何有意义的东西,那就是为什么我在这里问。
我会提供更多细节:我已经提到了几个链接: Permission based Authorization
此处作者按角色对声明进行分组。他正在使用 Identity Framework 创建角色并添加声明。但我不能使用它,因为我正在使用 AD 登录 jwt 令牌。此外,为了从 jwt 令牌中获取我的角色,我需要 HTTPContext,我无法在任何 class 中访问它,截至目前,它只能在 API Controller class 中访问,在那里我可以使用以下代码获取角色:
User.Identities.SelectMany(s => s.Claims).Where(s => s.Type.Contains("role")).Select(s => s.Value).FirstOrDefault()
因此,一旦我掌握了角色,我就会对我的数据库服务进行 httpclient get 调用,以获取与之相关的所有角色和权限。但是,我该如何动态授权我的 API 控制器 Rest 调用?
我想出了一些解决难题的方法。一种方法是通过 Custom Authorization Policy Providers using IAuthorizationPolicyProvider
在上述方法中,我会创建自定义策略属性,例如
[MinimumAccessAuthorize("Read","ModuleName1")]
[MinimumAccessAuthorize("Write","ModuleName2")]
在我的 Web API 操作方法之上,IAuthorizationPolicyProvider 会处理其余部分。这种方法的唯一问题是我需要硬编码属性以供 Read/Write 访问,此后每个控制器中的每个 god dam 操作方法都可用于模块。
解决这个问题的另一种方法是提出一个自定义中间件,在请求到达控制器之前对用户进行身份验证。
所以我创建了一个自定义中间件:Resource for custom middleware
在我的中间件中,我有一个名为 invokeAsync
的函数public async Task InvokeAsync(HttpContext context, [FromServices] IRoleService roleService, [FromServices] IPermissionService permissionService)
{
var roles = context.User.Identities.SelectMany(s => s.Claims).Where(s => s.Type.Contains("role")).Select(s => s.Value).FirstOrDefault();
if (roles == null)
{
context.Response.StatusCode = 401;
return;
}
var requestType = context.Request.Method;
var controller = context.Request.Path.Value.Replace("/api/v1/", "").Split('/')[0];
PermissionType typeRequired = requestType == "GET" ? PermissionType.Read : PermissionType.Write;
var (IsSuccessRole, roleResult) = await roleService.GetRoleAsync();
if (!IsSuccessRole)
{
context.Response.StatusCode = 500;
return;
}
int _roleId = roleResult.Where(r => r.RoleName == roles).Select(r => r.Id).FirstOrDefault();
var (IsSuccess, permissionResult) = await permissionService.GetPermissionAsync(_roleId);
if (!IsSuccess)
{
context.Response.StatusCode = 500;
return;
}
var validRoles = from p in permissionResult
join k in GetControllerModuleMaps()
on p.ProjectModule.ModuleName equals k.ModuleName
where k.ControllerName == controller && p.PermissionType >= typeRequired
select p.RoleId;
if (!validRoles.Any())
{
context.Response.StatusCode = 401;
return;
}
await next(context);
}
我正在传递 HTTP 上下文和 2 个 HTTP 客户端服务以从数据库中获取权限和角色数据。
我现在通过上下文和基本的 linq 查询检查 jwt 令牌中存在的用户角色。
然后进行基本验证以查看角色是否不为空。
我检查请求类型,然后查看它是 GET、PUT 还是 Patch 请求。根据请求,我将值存储为 PermissionType 中的枚举。
我将 REST 请求命中的控制器详细信息存储在控制器变量中。
现在,我使用角色服务在数据库中查询与我当前的 jwt 令牌角色关联的 roleId。然后,我使用权限服务从数据库中获取与该 roleID 关联的权限。
现在基于从数据库成功查询权限,我编写了一个 Linq 查询语法,其中我将来自数据库的权限结果数据与包含控制器名称和模块名称的 C# 记录列表连接起来值(不是密钥对值)。
示例记录列表如下所示:
var toRet = new List<ControllerRecord>();
toRet.Add(new ControllerRecord("Controller1", "Module 1"));
toRet.Add(new ControllerRecord("Controller1", "Module 2"));
我检查我的控制器记录是否与请求控制器路径匹配,并且与数据库中该角色关联的权限大于 Rest API 中请求的权限(这有效,因为我有枚举 NoAccess = 0 < GET request = Only Read = 1 < Put/Patch/Post request = Write = 2 )
如果有任何有效的角色,我让流程照常继续。
我就是这样把它拉过来的...