1

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

我已阅读(除其他外)以下文章:

我希望我的 web api 使用 Authorization 标头在标头中发送身份验证。我希望将此标头填充到名为AuthenticationToken. 然后,当我进行参数绑定时,我想检索这个先前创建的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

4

1 回答 1

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。

于 2013-01-17T18:19:36.933 回答