一、ABAC 简介
ABAC 即:【Attribute-based Access Control】基于属性的访问控制
与之对应的,我们有:【Role-based Access Control 】
可以参考这篇文章权限系统设计模型分析(DAC,MAC,RBAC,ABAC)
此处不再详细说明
二、Client 使用 ABAC
- 修改 IDP
-
Config
类
GetIdentityResources
方法添加代码
new IdentityResource("country","Country where the user lives in", new List<string>(){"country"}),
new IdentityResource("subscriptionlevel","User's subscription level",new List<string>(){"subscriptionlevel"})
GetClients
方法中的 AllowedScopes
添加
GetUsers
方法中为 Users 添加对应 claim 和 值
- 修改 Client
-
Startup
类
ConfigureServices
方法修改如下:
添加代码
services.AddAuthorization(authorizationOptions =>
{
authorizationOptions.AddPolicy(
"CanOrderFrame",
policyBuilder =>
{
policyBuilder.RequireAuthenticatedUser();
policyBuilder.RequireClaim("country", "Heaven", "CHN");
policyBuilder.RequireClaim("subscriptionlevel", "supervip");
});
});
定义一个 Policy:已登录用户并且付费级别为 supervip、国家是 Heaven 或 CHN的才满足条件
- 修改
_Layout.cshtml
当用户满足权限时才显示 OrderFrame 按钮
- 修改
GalleryController
为OrderFrame
方法添加属性
[Authorize(Policy = "CanOrderFrame")]
public async Task<IActionResult> OrderFrame(){...}
再运行,用不同用户登录尝试
例如,用 Claire 登录时 Debug 输出信息如下
点此查看github commit
三、API 使用 ABAC
- 上面使用的 Policy 比较简单,仅仅用到了 user claim。那么,如果需要更复杂的验证机制,比如用户编辑的图片是不是属于他,服务器当前时间是不是晚上等。
例如下面这几种情况
这个时候就需要编写复杂的验证 handler 了
- 新增类
MustOwnImageRequirement
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
namespace ImageGallery.API.Authorization
{
public class MustOwnImageRequirement: IAuthorizationRequirement
{
public MustOwnImageRequirement()
{
}
}
}
新增类 MustOwnImageHandler
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ImageGallery.API.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Filters;
namespace ImageGallery.API.Authorization
{
public class MustOwnImageHandler:AuthorizationHandler<MustOwnImageRequirement>
{
private readonly IGalleryRepository repository;
public MustOwnImageHandler(IGalleryRepository repo)
{
repository = repo;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MustOwnImageRequirement requirement)
{
var filterContext = context.Resource as AuthorizationFilterContext;
if (filterContext == null)
{
context.Fail();
return Task.CompletedTask;
}
var imageId = filterContext.RouteData.Values["id"].ToString();
if (!Guid.TryParse(imageId, out Guid imageGuid))
{
context.Fail();
return Task.CompletedTask;
}
var ownerId = context.User.Claims.FirstOrDefault(c => c.Type == "sub").Value;
if (!repository.IsImageOwner(imageGuid, ownerId)
&& context.User.Claims.FirstOrDefault(c => c.Type == "role").Value != "admin")
{
context.Fail();
return Task.CompletedTask;
}
context.Succeed(requirement);
return Task.CompletedTask;
}
}
}
Startup::ConfigureServices()
添加如下代码:
services.AddAuthorization(options =>
{
options.AddPolicy(
"MustOwnImage",
policyBuilder =>
{
policyBuilder.RequireAuthenticatedUser();
policyBuilder.AddRequirements(new MustOwnImageRequirement());
});
});
services.AddScoped<IAuthorizationHandler, MustOwnImageHandler>();
ImageController
在相关接口添加属性
[Authorize("MustOwnImage")]
如果想要在 API 获取额外的 claims,可按照如下修改 GetApiResources
运行即可检验代码生效
点此查看 github commit