如何将授权逻辑与控制器操作分开?

时间:2013-04-09 05:05:10

标签: c# asp.net-web-api authorization

给出以下代码:

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;
    }
}

将几乎所有方法体提取到授权过滤器中是否有意义?如果没有两次检索备份,我就没有办法做到这一点。

您如何将授权问题与控制器操作分开?

2 个答案:

答案 0 :(得分:3)

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

中的标头值

例如;

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

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

检查自定义AuthorizeAttribute

中的令牌
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: function (res, status, xhr) {
    model.requestToken = xhr.getResponseHeader('requestToken');
}

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

处理安全事项

答案 1 :(得分:0)

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

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