如何将授权逻辑与控制器动作分离

本文关键字:控制器 分离 授权 | 更新日期: 2023-09-27 18:08:04

给定以下代码:

public class BackupsController : ApiController
{
    private readonly IApiContext context;
    private readonly IBackupService backupService;
    public BackupsController(IApiContext context, IBackupService backupService)
    {
        this.context = context;
        this.backupService = backupService;
    }
    public HttpResponseMessage Get(Guid id)
    {
        if (id == Guid.Empty)
        {
            throw new HttpResponseException(HttpStatusCode.BadRequest);
        }
        IBackupView backup = backupService.Get(id);
        if (backup == null)
        {
            return Request.CreateErrorResponse(HttpStatusCode.NotFound, String.Format("BackupId '{0}' not found.", id));
        }
        if (!IsAuthorizedForBackup(backup))
        {
            throw new HttpResponseException(HttpStatusCode.Forbidden);
        }
        return Request.CreateResponse(HttpStatusCode.OK, backup);
    }
    private bool IsAuthorizedForBackup(IBackupView backup)
    {
        if (context.Principal.IsInRole(MembershipRole.Admin))
        {
            return true;
        }
        if (context.Principal.AllowDataSharing && backup.UserId == context.Principal.UserId)
        {
            return true;
        }
        if (backup.UserId == context.Principal.UserId && backup.Device.Uuid == context.DeviceUuid)
        {
            return true;
        }
        return false;
    }
}

将几乎所有方法体提取到授权过滤器中有意义吗?如果不检索备份两次,我想不出有什么办法做到这一点。

如何将授权关注从控制器操作中分离出来?

如何将授权逻辑与控制器动作分离

为了将安全逻辑与控制器逻辑分开,我更喜欢使用Http头在浏览器和控制器之间携带安全令牌,并在自定义AuthorizeAttribute中检查头值

例如

,

在JQuery ajax函数的beforeSend函数中设置安全令牌(之前从服务器获取的,见下文)

beforeSend: function (xhr) {
    xhr.setRequestHeader('requestToken', model.requestToken);
}

检查自定义AuthorizeAttribute中的token

public class AuthAttribute : AuthorizeAttribute
{
    public override void OnAuthorization(HttpActionContext actionContext)
    {
        var token = HttpContext.Current.Request.Headers["requestToken"];
        // Do the authorization based on token
    }
}

用自定义[Auth]属性装饰需要授权的控制器,如:

[Auth]
public class SomeController : ApiController

我们可以再次使用Http Headers向客户端发送新的令牌

HttpContext.Current.Response.Headers["requestToken"] = Guid.NewGuid();

在客户端,你可以将它存储在JQuery ajax函数的success函数中用于发送回请求

success: function (res, status, xhr) {
    model.requestToken = xhr.getResponseHeader('requestToken');
}

这可能不能完全处理您的情况,但主要思想是在Http头中携带(最好是加密)安全数据,并在自定义AuthorizeAttribute

中处理安全问题。

你所要求的在技术上是可行的。比如说,你实现了一个动作过滤器,并且在重写的onactionperformed中有一些逻辑将状态码设置为Forbidden。我没有这样做过,只是建议你探讨一下可行性。onactionexecute在action方法之后运行,它可以访问备份。

另一个更好的选择是使用基于声明的标识并实现ClaimsAuthorizationManager的子类。CheckAccess(AuthorizationContext)接受操作声明和资源声明。与备份相关的属性可以作为资源声明传递。