如何针对异常需求自定义 ASP.NET Web API AuthorizeAttribute [英] How to Customize ASP.NET Web API AuthorizeAttribute for Unusual Requirements

查看:37
本文介绍了如何针对异常需求自定义 ASP.NET Web API AuthorizeAttribute的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我从 System.Web.Http.AuthorizeAttribute 继承来创建自定义授权/身份验证例程,以满足使用 ASP.NET MVC 4 开发的 Web 应用程序的一些不寻常的要求.这增加了安全性用于来自 Web 客户端的 Ajax 调用的 Web API.要求是:

I am inheriting from System.Web.Http.AuthorizeAttribute to create a custom authorization/authentication routine to meet some unusual requirements for a web application developed using ASP.NET MVC 4. This adds security to the Web API used for Ajax calls from the web client. The requirements are:

  1. 用户每次执行交易时都必须登录以验证有人走后,其他人还没有走到工作站登录并离开.
  2. 无法在程序时将角色分配给 Web 服务方法.它们必须在运行时分配,以便管理员可以配置这个.此信息存储在系统数据库中.

Web 客户端是单页应用程序 (SPA),因此典型的表单身份验证效果不佳,但我正在尝试尽可能多地重用 ASP.NET 安全框架以满足要求.定制的 AuthorizeAttribute 非常适用于确定哪些角色与 Web 服务方法相关联的要求 2.我接受三个参数,应用程序名称、资源名称和操作来确定哪些角色与方法相关联.

The web client is a single page application (SPA) so the typical forms authentication does not work so well, but I am trying reuse as much of the ASP.NET security framework as I can to meet the requirements. The customized AuthorizeAttribute works great for requirement 2 on determining what roles are associated with a web service method. I accept three parameters, application name, resource name and operation to determine which roles are associated with a method.

public class DoThisController : ApiController
{
    [Authorize(Application = "MyApp", Resource = "DoThis", Operation = "read")]
    public string GetData()
    {
        return "We did this.";
    }
}

我重写了 OnAuthorization 方法以获取角色并对用户进行身份验证.由于用户必须为每笔交易进行身份验证,因此我通过在同一步骤中执行身份验证和授权来减少来回喋喋不休.我使用基本身份验证从 Web 客户端获取用户凭据,该身份验证在 HTTP 标头中传递加密凭据.所以我的 OnAuthorization 方法如下所示:

I override the OnAuthorization method to get the roles and authenticate the user. Since the user has to be authenticated for each transaction I reduce the back and forth chatter by performing authentication and authorization in the same step. I get the users credentials from the web client by using basic authentication which passes the encrypted credentials in the HTTP header. So my OnAuthorization method looks like this:

public override void OnAuthorization(HttpActionContext actionContext)
{

     string username;
     string password;
     if (GetUserNameAndPassword(actionContext, out username, out password))
     {
         if (Membership.ValidateUser(username, password))
         {
             FormsAuthentication.SetAuthCookie(username, false);
             base.Roles = GetResourceOperationRoles();
         }
         else
         {
             FormsAuthentication.SignOut();
             base.Roles = "";
         }
     }
     else
     {
         FormsAuthentication.SignOut();
         base.Roles = "";
     }
     base.OnAuthorization(actionContext);
 }

GetUserNameAndPassword 从 HTTP 标头中检索凭据.然后我使用 Membership.ValidateUser 来验证凭据.我插入了一个自定义成员资格提供程序和角色提供程序以访问自定义数据库.如果用户通过身份验证,我将检索资源和操作的角色.从那里我使用基础 OnAuthorization 来完成授权过程.这就是它崩溃的地方.

GetUserNameAndPassword retrieves the credentials from the HTTP header. I then use the Membership.ValidateUser to validate the credentials. I have a custom membership provider and role provider plugged in to hit a custom database. If the user is authenticated I then retrieve the roles for the resource and operation. From there I use the base OnAuthorization to complete the authorization process. Here is where it breaks down.

如果用户通过身份验证,我使用标准表单身份验证方法登录用户 (FormsAuthentication.SetAuthCookie),如果失败,我将其注销 (FormsAuthentication.SignOut).但问题似乎是基础 OnAuthorization 类无法访问更新后的 Principal,以便将 IsAuthenticated 设置为正确的值.它总是落后一步.我的猜测是它使用了一些缓存值,直到有一个到 Web 客户端的往返行程才会更新.

If the user is authenticated I use the standard forms authentication methods to log the user in (FormsAuthentication.SetAuthCookie) and if they fail I log them out (FormsAuthentication.SignOut). But the problem seems to be that base OnAuthorization class does not have access to Principal that is updated so that IsAuthenticated is set to the correct value. It is always one step behind. And my guess is that it is using some cached value that does not get updated until there is a round trip to the web client.

所以所有这些都引出了我的具体问题,即是否有另一种方法可以在不使用 cookie 的情况下将 IsAuthenticated 设置为当前 Principal 的正确值?在我看来,cookie 并不真正适用于我每次都必须进行身份验证的特定场景.我知道 IsAuthenticated 未设置为正确值的原因是我还将 HandleUnauthorizedRequest 方法重写为:

So all of this leads up to my specific question which is, is there another way to set IsAuthenticated to the correct value for the current Principal without using cookies? It seems to me that cookies do not really apply in this specific scenario where I have to authenticate every time. The reason I know IsAuthenticated is not set to the correct value is I also override the HandleUnauthorizedRequest method to this:

 protected override void HandleUnauthorizedRequest(HttpActionContext filterContext)
 {
     if (((System.Web.HttpContext.Current.User).Identity).IsAuthenticated)
     {
         filterContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
     }
     else
     {
         base.HandleUnauthorizedRequest(filterContext);
     }
 }

如果失败是因为授权而不是身份验证,这允许我向 Web 客户端返回 Forbidden 状态代码,并且它可以做出相应的响应.

This allows me to return a status code of Forbidden to the web client if the failure was because of authorization instead of authentication and it can respond accordingly.

那么在这种情况下,为当前原则设置IsAuthenticated的正确方法是什么?

So what is the proper way to set IsAuthenticated for the current Principle in this scenario?

推荐答案

对于我的场景来说,最好的解决方案似乎是完全绕过基本的 OnAuthorization.由于我每次都必须对 cookie 和缓存进行身份验证,因此该原理没有多大用处.所以这是我想出的解决方案:

The best solution for my scenario appears to be bypass the base OnAuthorization completely. Since I have to authenticate each time cookies and caching the principle are not of much use. So here is the solution I came up with:

public override void OnAuthorization(HttpActionContext actionContext)
{
    string username;
    string password;

    if (GetUserNameAndPassword(actionContext, out username, out password))
    {
        if (Membership.ValidateUser(username, password))
        {
            if (!isUserAuthorized(username))
                actionContext.Response = 
                    new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
        }
        else
        {
            actionContext.Response = 
                new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
        }
    }
    else
    {
        actionContext.Response = 
            new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest);
    }
}

我开发了自己的方法来验证名为 isUserAuthorized 的角色,我不再使用基本的 OnAuthorization,因为它检查了当前的原则 查看它是否已认证.IsAuthenticated 只允许获取,所以我不确定如何设置它,而且我似乎不需要当前的原则.对此进行了测试,效果很好.

I developed my own method for validating the roles called isUserAuthorized and I am not using the base OnAuthorization any more since it checks the current Principle to see if it isAuthenticated. IsAuthenticated only allows gets so I am not sure how else to set it, and I do not seem to need the current Principle. Tested this out and it works fine.

如果有人有更好的解决方案或可以看到此解决方案的任何问题,仍然感兴趣.

Still interested if anyone has a better solution or can see any issues with this this one.

这篇关于如何针对异常需求自定义 ASP.NET Web API AuthorizeAttribute的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆