【问题标题】:Where do I handle custom authentication and authorization in webapi?我在哪里处理 webapi 中的自定义身份验证和授权?
【发布时间】:2013-01-01 08:01:26
【问题描述】:

注意:我在下面的代码 cmets 中有很多问题。我也需要这些问题的答案。

我已阅读(其中包括)以下文章:

我希望我的 Web api 使用 Authorization 标头在标头中发送身份验证。我希望将此标头填充到名为 AuthenticationToken 的 c# 类中。然后,当我进行参数绑定时,我想检索这个先前创建的 AuthenticationToken 对象并将其传递给我的控制器操作。例如,如果我有以下控制器

public class MyServiceController : ApiController {

    readonly ISecurityService _security;
    readonly IMyService _myService;

    // constructor values are injected
    public MyServiceController(ISecurityService security, IMyService myService) {
        _security = security;
        _myService = myService;
    }

    public SomeData GetASpecificItem(AuthenticationToken token, int id) {

       if (_security.IsAuthorized(token, Permissions.Read)) {
           return myService.DoStuffToGetSomeData(token);
       } else {
          var msg = new HttpResponseMessage(HttpStatusCode.Forbidden);
          throw new HttpResponseException(msg);
       }
    }
}

和下面的参数绑定类

public class AuthenticationTokenParameterBinding 
  : HttpParameterBinding { // do I need to inherit from a different class?

  public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider,
                                           HttpActionContext actionContext,
                                           CancellationToken cancellationToken) {
    try {

      AuthenticationToken token; // UPDATED: how can i get this from the data 
                                 //          available from inside this method?

      SetValue(actionContext, token);

      // is this the correct task to return on successfull parameter binding?
      return base.ExecuteBindingAsyn(metadataProvider, actionContext, cancellationToken);

    } catch {
      return Task<HttpResponseMessage>.Factory.StartNew(() => {
        var hpm = new HttpResponseMessage(HttpStatusCode.Unauthorized);
        hpm.Headers.Add("WWW-Authenticate","MyCustomScheme");
        return hpm;
      });
    }
  }
}

如果这两个实现正确,那么控制器会自动获取授权时创建的AuthenticationToken实例。

我不知道在此过程之前在哪里进行身份验证。我也不知道如何在身份验证和授权之间传递对象。

更新: 我不能使用自定义AuthorizeAttribute,因为授权可能针对对象:

public SaveResponse Save(AuthenticationToken user, SomeObjectThatNeedsToBeSaved obj) {

  // NOTE: permissions are checked between the object and the user, not a role

  if (_security.IsAuthorized(user, obj, Permission.Modify, Permission.Create)) {

     // NOTE: other permissions we don't know about may need to be checked in the service call

     return new SaveResponse {
         Success = ISomeService.Save(user, obj); // bool return value
     }
  } else {
     // return 403 Forbidden      }
}

我需要将令牌传递给控制器​​操作,但我还需要在令牌传递给控制器​​之前对其进行身份验证。由于所有这些都不一定是基于角色的,我看不到如何从自定义 AuthorizeAttribute 内部进行身份验证

【问题讨论】:

    标签: authentication asp.net-mvc-4 asp.net-web-api authorization


    【解决方案1】:

    我使用自定义 AuthorizeAttribute 来处理 Web API 的身份验证和授权。此属性用作过滤器,并将在请求到达您的 Web API 方法之前对其进行处理。在重写的 OnAuthorize 方法中,如果身份验证失败,您可以返回 HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized)HttpResponseMessage(System.Net.HttpStatusCode.Forbidden) 如果授权失败,以便客户端可以区分这两种类型的错误。除了自定义 AuthorizeAttribute 之外,我还实现了自定义 MembershipProviderRoleProvider 来处理我的特定安全要求和自定义数据库架构。

    我使用基本身份验证来传递授权凭据。这会将凭据放在标题中。使用 JQuery ajax 函数的 beforeSend 事件处理程序非常简单。以下是如何执行此操作的示例。

        getAuthorizationHeader = function (username, password) {
          var authType;
          var up = $.base64.encode(username + ":" + password);
          authType = "Basic " + up;
        };
        return authType;
     };
    
        $.ajax({
            url: _url,
            data: _data,
            type: _type,
            beforeSend: function (xhr) {
                xhr.setRequestHeader("Authorization", getAuthorizationHeader(username, password));
            },
            success: ajaxSuccessHandler,
            error: ajaxErrHandler
        });
    

    这对在标头中发送的用户名/密码进行编码。请注意,仅依靠编码是不够的安全性,因为它很容易解码。您仍然希望使用 HTTPS/SSL 来确保通过网络发送的信息是安全的。

    在 Web API 方面,您可以创建一个自定义 AuthorizeAttribute,它从标头获取凭据、对其进行解码并执行您的授权过程。 Web API 使用了一个单独的 AuthorizeAttribute,而不是控制器。在创建自定义 AuthorizeAttribute 时,请务必使用 System.Web.Http.AuthorizeAttribute 作为基类。他们有不同的行为。用于控制器的那个将要重定向到登录页面,而用于 Web API 的那个会返回一个指示成功或失败的 HTTP 代码。如果授权未能区分由于授权而不是身份验证导致的失败,我将返回一个 HTTP 代码 Forbidden,以便客户端可以做出相应的反应。

    这是一个从标头中获取凭据的示例方法,可以在自定义 AuthorizeAttribute 中使用。

        private bool GetUserNameAndPassword(HttpActionContext actionContext, out string username, out string password)
        {
            bool gotIt = false;
            username = string.Empty;
            password = string.Empty;
            IEnumerable<string> headerVals;
            if (actionContext.Request.Headers.TryGetValues("Authorization", out headerVals))
            {
                try
                {
                    string authHeader = headerVals.FirstOrDefault();
                    char[] delims = { ' ' };
                    string[] authHeaderTokens = authHeader.Split(new char[] { ' ' });
                    if (authHeaderTokens[0].Contains("Basic"))
                    {
                        string decodedStr = SecurityHelper.DecodeFrom64(authHeaderTokens[1]);
                        string[] unpw = decodedStr.Split(new char[] { ':' });
                        username = unpw[0];
                        password = unpw[1];
                    }
                    gotIt = true;
                }
                catch { gotIt = false; }
            }
    
            return gotIt;
        }
    

    这是用于解码此方法中使用的标头数据的代码。

        public static string DecodeFrom64(string encodedData)
        {
    
            byte[] encodedDataAsBytes
    
                = System.Convert.FromBase64String(encodedData);
    
            string returnValue =
    
               System.Text.Encoding.ASCII.GetString(encodedDataAsBytes);
    
            return returnValue;
    
        }
    

    获得用户名和密码后,您就可以执行授权过程并将相应的 HTTP 代码返回给客户端进行处理。

    您可以使用自定义令牌执行类似的过程,或者如果您不想将密码/用户名存储在客户端中,则可以利用来回传递的 cookie。

    【讨论】:

    • 我无法使用基于角色的安全性。这种方法行不通。这就是为什么我试图按照我在问题中提出的方式来做。
    • 此方法不排除基于角色的授权方法。如果你不使用角色,你授权什么?
    • 反对对象,例如此用户是否具有 xyz 对象的修改权限。另外,我的问题是关于身份验证,而不是授权。授权在代码中完成。这就是为什么我需要方法签名中的身份验证令牌。
    • 由于问题的标题,我假设两者都是。您可以像我描述的那样在同一个庄园中处理身份验证。您可以将其更改为通过令牌而不是编码的用户名/密码发送。抱歉,我只是不明白您要对方法签名中的令牌做什么。
    猜你喜欢
    • 1970-01-01
    • 2017-06-04
    • 2016-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多