Authorization 具有处理程序的通用授权
我正在尝试在我的ASP.NET Core 2.0 Web应用程序中实现授权 该应用程序有20个型号,每个型号都有一个控制器,至少实现一个CRUD。我发现了这些,我喜欢使用处理程序来授权请购单的想法。我首先希望实现用户授权,即用户只有查看/编辑自己实体的权限。我的所有数据库实体都有一个OwnerId字段 我发现这些示例似乎只适用于一个特定的控制器Authorization 具有处理程序的通用授权,authorization,asp.net-core-2.0,Authorization,Asp.net Core 2.0,我正在尝试在我的ASP.NET Core 2.0 Web应用程序中实现授权 该应用程序有20个型号,每个型号都有一个控制器,至少实现一个CRUD。我发现了这些,我喜欢使用处理程序来授权请购单的想法。我首先希望实现用户授权,即用户只有查看/编辑自己实体的权限。我的所有数据库实体都有一个OwnerId字段 我发现这些示例似乎只适用于一个特定的控制器 [HttpGet("{id}"] public async Task<IActionResult> GetById(int id) {
[HttpGet("{id}"]
public async Task<IActionResult> GetById(int id)
{
var stringGuid = User.Claims.FirstOrDefault(c => c.Type == "sub")?.Value;
if (String.IsNullOrWhiteSpace(stringGuid)) return Unauthorized();
var ownerGuid = new Guid(stringGuid);
var entity = _yourCrudInstance.GetById(id, ownerGuid);
return Ok(entity);
}
因此,我的问题是:是否可以为所有控制器创建一个授权处理程序?您是否找到了一个解决方案或解决方案,可以使用授权处理程序或授权属性?我的设置和你的完全一样 我试图创建一个通用属性来服务于所有可能的实体CRUD所有者检查,但设计不允许使用通用属性 我提出的唯一两个(不令人满意的)解决方案是:
[HttpGet("{id}"]
public async Task<IActionResult> GetById(int id)
{
var stringGuid = User.Claims.FirstOrDefault(c => c.Type == "sub")?.Value;
if (String.IsNullOrWhiteSpace(stringGuid)) return Unauthorized();
var ownerGuid = new Guid(stringGuid);
var entity = _yourCrudInstance.GetById(id, ownerGuid);
return Ok(entity);
}
提供参数ownerId。GetById(id,ownerGuid)
,前提是没有重载方法,或者代码没有编译
更新:
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
[Route("api/yourcontroller")]
public class YourApiController : Controller
{
private readonly YourEntityXYZRepository _repo;
public YourApiController(YourDbContext yourDbContext)
{
_repo = new YourEntityXYZRepository(yourDbContext);
}
[HttpGet("{id}")]
[AuthorizeOwnerIntId(typeof(YourEntityXYZRepository), Policy = "YourCustomPolicy")]
public async Task<IActionResult> GetById(int id)
{
var entity = _repo.GetById(id);
return Ok(entity);
}
}
// The "generic" authorization attribute for type int id
// Similar authorization attributes for every type of id must be created additionally, for example Guid
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class AuthorizeOwnerIntIdAttribute : AuthorizeAttribute, IAuthorizationFilter
{
private object _entityRepositoryObject;
private IAsyncOwnerIntId _entityRepository;
private readonly Type _TCrudRepository;
public AuthorizeOwnerIntIdAttribute(Type TCrudRepository)
{
_TCrudRepository = TCrudRepository;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var yourDbContext = context.HttpContext.RequestServices.GetService<YourDbContext>();
_entityRepositoryObject = Activator.CreateInstance(_TCrudRepository, yourDbContext);
_entityRepository = _entityRepositoryObject as IAsyncOwnerIntId;
var user = context.HttpContext.User;
if (!user.Identity.IsAuthenticated)
{
// it isn't needed to set unauthorized result
// as the base class already requires the user to be authenticated
// this also makes redirect to a login page work properly
// context.Result = new UnauthorizedResult();
return;
}
// get entityId from uri
var idString = context.RouteData.Values["id"].ToString();
if (!int.TryParse(idString, out var entityId))
{
context.Result = new UnauthorizedResult();
return;
}
// get subjectId from user claims
var ownerIdString = context.HttpContext.User.Claims.FirstOrDefault(c => c.Type == "sub")?.Value;
if (!Guid.TryParse(ownerIdString, out var ownerGuid))
{
context.Result = new UnauthorizedResult();
return;
}
if (!_entityRepository.IsEntityOwner(entityId, ownerGuid))
{
context.Result = new UnauthorizedResult();
}
}
}
// Your concrete repository
public class YourEntityXYZRepository : AsyncCrud<YourEntityXYZ, int>,
IAsyncOwnerIntId // Note that type concrete IAsyncOwnerIntId is only implemented in concrete repository
{
public YourEntityXYZRepository(YourDbContext yourDbContext) : base(yourDbContext)
{
}
}
// Your generic Crud repository
public abstract class AsyncCrud<TEntity, TId> : IAsyncCrud<TEntity, TId>
where TEntity : class, IEntityUniqueIdentifier<TId>, IEntityOwner
where TId : struct
{
protected YourDbContext YourDbContext;
public AsyncCrud(YourDbContext yourDbContext)
{
YourDbContext = yourDbContext;
}
// Note that the following single concrete implementation satisfies both interface members
// bool IsEntityOwner(TId id, Guid ownerGuid); from IAsyncCrud<TEntity, TId> and
// bool IsEntityOwner(int id, Guid ownerGuid); from IAsyncOwnerIntId
public bool IsEntityOwner(TId id, Guid ownerGuid)
{
var entity = YourDbContext.Set<TEntity>().Find(id);
if (entity != null && entity.OwnerGuid == ownerGuid)
{
return true;
}
return false;
}
// Further implementations (redacted)
public Task<bool> SaveContext() { throw new NotImplementedException(); }
public Task<TEntity> Update(TEntity entity){ throw new NotImplementedException(); }
public Task<TEntity> Create(TEntity entity, Guid ownerGuid) { throw new NotImplementedException(); }
public Task<bool> Delete(TId id) { throw new NotImplementedException(); }
public Task<bool> DoesEntityExist(TId id) { throw new NotImplementedException(); }
public virtual Task<TEntity> GetById(TId id) { throw new NotImplementedException(); }
}
// The interface for the Crud operations
public interface IAsyncCrud<TEntity, TId>
where TEntity : class, IEntityUniqueIdentifier<TId>
where TId : struct
{
bool IsEntityOwner(TId id, Guid ownerGuid);
Task<bool> DoesEntityExist(TId id);
Task<TEntity> GetById(TId id);
Task<TEntity> Create(TEntity entity, Guid ownerGuid);
Task<TEntity> Update(TEntity entity);
Task<bool> Delete(TId id);
Task<bool> SaveContext();
}
// The interface for the concrete type method for int id
// Similar interfaces for every type of id must be created additionally, for example Guid
public interface IAsyncOwnerIntId
{
bool IsEntityOwner(int id, Guid ownerGuid);
}
// Typical db context
public class YourDbContext : DbContext
{
public YourDbContext(DbContextOptions<YourDbContext> options) : base(options)
{
}
public DbSet<YourEntityXYZ> YourEntityXYZ { get; set; }
}
public class YourEntityXYZ : IEntityUniqueIdentifier<int>, IEntityOwner
{
public int Id { get; set; }
public Guid? OwnerGuid { get; set; }
// ... Additonal custom properties
}
public interface IEntityUniqueIdentifier<TId>
where TId : struct
{
TId Id { get; set; }
}
public interface IEntityOwner
{
Guid? OwnerGuid { get; set; }
}
使用系统;
使用System.Linq;
使用System.Threading.Tasks;
使用Microsoft.AspNetCore.Authorization;
使用Microsoft.AspNetCore.Mvc;
使用Microsoft.AspNetCore.Mvc.Filters;
使用Microsoft.EntityFrameworkCore;
使用Microsoft.Extensions.DependencyInjection;
[路由(“api/yourcontroller”)]
公共类YourApicController:控制器
{
私人只读YourEntityXYZRepository\u repo;
公共YourApicController(YourDbContext YourDbContext)
{
_repo=新的YourEntityXYZRepository(yourDbContext);
}
[HttpGet(“{id}”)]
[AuthorizeOwnerIntId(typeof(YourEntityXYZRepository),Policy=“YourCustomPolicy”)]
公共异步任务GetById(int id)
{
var实体=_repo.GetById(id);
返回Ok(实体);
}
}
//int-id类型的“通用”授权属性
//此外,还必须为每种类型的id创建类似的授权属性,例如Guid
[AttributeUsage(AttributeTargets.Method,AllowMultiple=true,Inherited=true)]
公共类AuthorizeOwnerIntIdAttribute:AuthorizeAttribute,IAAuthorizationFilter
{
私有对象_entityRepositoryObject;
私人IAsyncOwnerIntId_entityRepository;
私有只读类型\u tcrud存储库;
public AuthorizeOwnerIntIdAttribute(类型TCrudRepository)
{
_TCrudRepository=TCrudRepository;
}
授权时的公共无效(AuthorizationFilterContext上下文)
{
var yourDbContext=context.HttpContext.RequestServices.GetService();
_entityRepositoryObject=Activator.CreateInstance(\u TCrudRepository,yourDbContext);
_entityRepository=\u entityRepository对象作为IAsyncOwnerIntId;
var user=context.HttpContext.user;
如果(!user.Identity.IsAuthenticated)
{
//不需要设置未经授权的结果
//因为基类已经要求对用户进行身份验证
//这也使得重定向到登录页面正常工作
//context.Result=新的UnauthorizedResult();
返回;
}
//从uri获取entityId
var idString=context.RouteData.Values[“id”].ToString();
如果(!int.TryParse(idString,out var entityId))
{
context.Result=新的UnauthorizedResult();
返回;
}
//从用户声明中获取主体
var ownerIdString=context.HttpContext.User.Claims.FirstOrDefault(c=>c.Type==“sub”)?.Value;
if(!Guid.TryParse(owneristring,out var ownerGuid))
{
context.Result=新的UnauthorizedResult();
返回;
}
if(!\u entityRepository.IsEntityOwner(entityId,ownerGuid))
{
context.Result=新的UnauthorizedResult();
}
}
}
//你的混凝土仓库
公共类YourEntityXYZRepository:AsyncCrud,
IAsyncOwnerIntId//注意,类型concrete IAsyncOwnerIntId仅在concrete存储库中实现
{
公共YourEntityXYZRepository(YourDbContext YourDbContext):基(YourDbContext)
{
}
}
//您的通用Crud存储库
公共抽象类AsyncCrud:IAsyncCrud
其中tenty:类、IEntityUniqueIdentifier、IEntityOwner
其中TId:struct
{
保护YourDbContext YourDbContext;
公共异步CRUD(YourDbContext YourDbContext)
{
YourDbContext=YourDbContext;
}
//请注意,以下单个具体实现满足两个接口成员
//bool IsEntityOwner(TId id,Guid ownerGuid);来自IAsyncrud和
//bool IsEntityOwner(int-id,Guid-ownerGuid);来自IAsyncOwnerIntId
公共bool iEntityOwner(TId id、Guid ownerGuid)
{
var entity=YourDbContext.Set().Find(id)