我想用ASP构建一个基于rest的web服务。. NET Web API,第三方开发人员将使用它来访问我的应用程序的数据。

我读了很多关于OAuth的文章,它似乎是标准的,但是找到一个好的示例文档来解释它是如何工作的(这实际上是可以工作的!)似乎是非常困难的(特别是对于一个OAuth新手)。

是否有一个实际构建和工作的示例,并显示如何实现它?

我下载了大量的样本:

DotNetOAuth——从新手的角度来看,文档是没有希望的 Thinktecture -不能让它建立

我也看过一些博客,他们提出了一个简单的基于令牌的方案(就像这样)——这看起来像是在重新发明轮子,但它的优点是在概念上相当简单。

似乎在SO上有很多这样的问题,但没有好的答案。

大家都在这里做什么?


当前回答

如果你想以服务器到服务器的方式保护你的API(没有重定向到网站的两腿认证)。您可以查看OAuth2客户端凭证授予协议。

https://dev.twitter.com/docs/auth/application-only-auth

我开发了一个库,可以帮助您轻松地将这种支持添加到您的WebAPI。你可以将其作为NuGet包安装:

https://nuget.org/packages/OAuth2ClientCredentialsGrant/1.0.0.0

这个库的目标是。net Framework 4.5。

一旦将包添加到项目中,它将在项目的根目录中创建一个自述文件。您可以查看该自述文件以了解如何配置/使用此包。

干杯!

其他回答

我建议首先从最直接的解决方案开始——也许简单的HTTP基本身份验证+ HTTPS在您的场景中就足够了。

如果不是(比如你不能使用https,或者需要更复杂的密钥管理),你可以看看其他人建议的基于hmac的解决方案。这样的API的一个很好的例子是Amazon S3 (http://s3.amazonaws.com/doc/s3-developer-guide/RESTAuthentication.html)

我写过一篇关于在ASP中基于HMAC的身份验证的博文。NET Web API。它讨论了Web API服务和Web API客户端,并在bitbucket上提供了代码。http://www.piotrwalat.net/hmac-authentication-in-asp-net-web-api/

这是一篇关于Web API中的基本身份验证的文章:http://www.piotrwalat.net/basic-http-authentication-in-asp-net-web-api-using-message-handlers/

记住,如果你要向第三方提供API,你也很可能要负责交付客户端库。基本身份验证在这里有一个显著的优势,因为大多数编程平台都支持它开箱即用。另一方面,HMAC并不是那么标准化,需要自定义实现。这些应该是相对简单的,但仍然需要工作。

PS.也可以选择使用HTTPS +证书。http://www.piotrwalat.net/client-certificate-authentication-in-asp-net-web-api-and-windows-store-apps/

Web API引入了一个属性[Authorize]来提供安全性。可以全局设置(global.asx)

public static void Register(HttpConfiguration config)
{
    config.Filters.Add(new AuthorizeAttribute());
}

或每个控制器:

[Authorize]
public class ValuesController : ApiController{
...

当然,您的身份验证类型可能会有所不同,您可能想要执行自己的身份验证,当这种情况发生时,您可能会发现从授权属性继承并扩展它以满足您的需求是有用的:

public class DemoAuthorizeAttribute : AuthorizeAttribute
{
    public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
        if (Authorize(actionContext))
        {
            return;
        }
        HandleUnauthorizedRequest(actionContext);
    }

    protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
        var challengeMessage = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
        challengeMessage.Headers.Add("WWW-Authenticate", "Basic");
        throw new HttpResponseException(challengeMessage);
    }

    private bool Authorize(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
        try
        {
            var someCode = (from h in actionContext.Request.Headers where h.Key == "demo" select h.Value.First()).FirstOrDefault();
            return someCode == "myCode";
        }
        catch (Exception)
        {
            return false;
        }
    }
}

在你的控制器中:

[DemoAuthorize]
public class ValuesController : ApiController{

下面是WebApi授权的其他自定义实现的链接:

http://www.piotrwalat.net/basic-http-authentication-in-asp-net-web-api-using-membership-provider/

你试过DevDefined.OAuth吗?

我用它来保护我的WebApi与2- legs OAuth。我还成功地用PHP客户端测试了它。

使用这个库可以很容易地添加对OAuth的支持。下面是如何实现ASP的提供程序。NET MVC Web API:

1)获取DevDefined的源代码。OAuth: https://github.com/bittercoder/DevDefined.OAuth——最新版本允许OAuthContextBuilder扩展。

2)构建库并在Web API项目中引用它。

3)创建一个自定义上下文构建器来支持从HttpRequestMessage构建上下文:

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net.Http;
using System.Web;

using DevDefined.OAuth.Framework;

public class WebApiOAuthContextBuilder : OAuthContextBuilder
{
    public WebApiOAuthContextBuilder()
        : base(UriAdjuster)
    {
    }

    public IOAuthContext FromHttpRequest(HttpRequestMessage request)
    {
        var context = new OAuthContext
            {
                RawUri = this.CleanUri(request.RequestUri), 
                Cookies = this.CollectCookies(request), 
                Headers = ExtractHeaders(request), 
                RequestMethod = request.Method.ToString(), 
                QueryParameters = request.GetQueryNameValuePairs()
                    .ToNameValueCollection(), 
            };

        if (request.Content != null)
        {
            var contentResult = request.Content.ReadAsByteArrayAsync();
            context.RawContent = contentResult.Result;

            try
            {
                // the following line can result in a NullReferenceException
                var contentType = 
                    request.Content.Headers.ContentType.MediaType;
                context.RawContentType = contentType;

                if (contentType.ToLower()
                    .Contains("application/x-www-form-urlencoded"))
                {
                    var stringContentResult = request.Content
                        .ReadAsStringAsync();
                    context.FormEncodedParameters = 
                        HttpUtility.ParseQueryString(stringContentResult.Result);
                }
            }
            catch (NullReferenceException)
            {
            }
        }

        this.ParseAuthorizationHeader(context.Headers, context);

        return context;
    }

    protected static NameValueCollection ExtractHeaders(
        HttpRequestMessage request)
    {
        var result = new NameValueCollection();

        foreach (var header in request.Headers)
        {
            var values = header.Value.ToArray();
            var value = string.Empty;

            if (values.Length > 0)
            {
                value = values[0];
            }

            result.Add(header.Key, value);
        }

        return result;
    }

    protected NameValueCollection CollectCookies(
        HttpRequestMessage request)
    {
        IEnumerable<string> values;

        if (!request.Headers.TryGetValues("Set-Cookie", out values))
        {
            return new NameValueCollection();
        }

        var header = values.FirstOrDefault();

        return this.CollectCookiesFromHeaderString(header);
    }

    /// <summary>
    /// Adjust the URI to match the RFC specification (no query string!!).
    /// </summary>
    /// <param name="uri">
    /// The original URI. 
    /// </param>
    /// <returns>
    /// The adjusted URI. 
    /// </returns>
    private static Uri UriAdjuster(Uri uri)
    {
        return
            new Uri(
                string.Format(
                    "{0}://{1}{2}{3}", 
                    uri.Scheme, 
                    uri.Host, 
                    uri.IsDefaultPort ?
                        string.Empty :
                        string.Format(":{0}", uri.Port), 
                    uri.AbsolutePath));
    }
}

4)使用本教程创建OAuth提供者:http://code.google.com/p/devdefined-tools/wiki/OAuthProvider。在最后一步(访问受保护资源示例)中,您可以在AuthorizationFilterAttribute属性中使用以下代码:

public override void OnAuthorization(HttpActionContext actionContext)
{
    // the only change I made is use the custom context builder from step 3:
    OAuthContext context = 
        new WebApiOAuthContextBuilder().FromHttpRequest(actionContext.Request);

    try
    {
        provider.AccessProtectedResourceRequest(context);

        // do nothing here
    }
    catch (OAuthException authEx)
    {
        // the OAuthException's Report property is of the type "OAuthProblemReport", it's ToString()
        // implementation is overloaded to return a problem report string as per
        // the error reporting OAuth extension: http://wiki.oauth.net/ProblemReporting
        actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
            {
               RequestMessage = request, ReasonPhrase = authEx.Report.ToString()
            };
    }
}

我已经实现了我自己的提供者,所以我还没有测试上面的代码(当然除了我在我的提供者中使用的WebApiOAuthContextBuilder),但它应该可以正常工作。

如果你想以服务器到服务器的方式保护你的API(没有重定向到网站的两腿认证)。您可以查看OAuth2客户端凭证授予协议。

https://dev.twitter.com/docs/auth/application-only-auth

我开发了一个库,可以帮助您轻松地将这种支持添加到您的WebAPI。你可以将其作为NuGet包安装:

https://nuget.org/packages/OAuth2ClientCredentialsGrant/1.0.0.0

这个库的目标是。net Framework 4.5。

一旦将包添加到项目中,它将在项目的根目录中创建一个自述文件。您可以查看该自述文件以了解如何配置/使用此包。

干杯!

继续@ Cuong Le的回答,我防止重放攻击的方法是

//在客户端使用共享私钥(或用户密码)加密Unix时间

//将它作为请求头的一部分发送到服务器(WEB API)

//使用共享私钥(或用户密码)解密服务器上的Unix时间(WEB API)

//检查客户端Unix时间和服务器Unix时间之间的时间差,不应大于x秒

//如果用户ID/哈希密码是正确的,并且解密的UnixTime在服务器时间的x秒内,那么它是一个有效的请求