四、使用 OAuth2 保护 Web API

本章解释了如何使用 OAuth2 来保护一个 web API,通过使用 OWIN 中间件对成员数据库进行认证。 您将能够使用本地登录使用 OAuth2 发送经过认证的请求。

在本章中,我们将涵盖以下主题:

  • 在 IIS 中托管 OWIN 并将 Web API 添加到 OWIN 管道中
  • 个人用户帐户登录认证流程
  • 发送未经授权的请求
  • 获取访问令牌
  • 发送经过验证的请求

在 IIS 中托管 OWIN,并向 OWIN 管道中添加 Web API

让我们创建一个空的 Web API 模板来集成 ASP。 净的身份。 遵循以下步骤:

  1. 在 Visual Studio 的Start页面中创建新项目
  2. 选择 Visual c# Installed Template 命名为Web
  3. 选择ASP.NET Web 应用
  4. Name the project ContactLookupOwin and click OK:

    Hosting OWIN in IIS and adding Web API to the OWIN pipeline

    图 1 -我们已经命名了 ASP.NET Web 应用作为“ContactLookupOwin”

  5. Select the Empty template in the New ASP.NET Project dialog and click OK:

    Hosting OWIN in IIS and adding Web API to the OWIN pipeline

    图 2 -为应用“ContactLookupOwin”选择空模板

  6. Install NuGet packages for the OWIN server that enables OWIN-based applications to run on IIS using the ASP.NET request pipeline:

    Hosting OWIN in IIS and adding Web API to the OWIN pipeline

    图 3 -已安装的 NuGet 包“microsoft . owen . host . systemweb”

  7. Right-click on Project and select Add New Item in the ContactLookupOwin project. Then, select the OWIN startup class in the center pane and name the class file OwinStartup.cs:

    Hosting OWIN in IIS and adding Web API to the OWIN pipeline

    图 4 -添加了名为“OwinStartup.cs”的 OWIN 启动类文件

  8. 用以下代码替换OwinStartup类中的Configuration方法:

这段代码配置响应的内容类型,写入响应体。 当服务器接收到 HTTP 请求时,OWIN 管道会调用中间件。

通过按F5运行应用,您将在浏览器中看到以下输出:

Hosting OWIN in IIS and adding Web API to the OWIN pipeline

图 5 -响应注入内容“Owin Startup”

按照给出的步骤将 Web API 添加到 OWIN 管道:

  1. 在包管理控制台运行如下命令安装 NuGet 包Microsoft.AspNet.WebApi.OwinSelfHost:

    Install-Package Microsoft.AspNet.WebApi.OwinSelfHost

  2. 这个包使应用能够托管 ASP.NET Web API 在我们的进程中使用 OWIN HttpListener 服务器。

  3. 修改Owin Startup类中的Configuration方法,代码如下:

    ``` public void Configuration(IAppBuilder app) { HttpConfiguration config = new HttpConfiguration(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } );

            app.UseWebApi(config);
    
            app.Run(context =>
            {
                context.Response.ContentType = "text/plain";
                return context.Response.WriteAsync("Owin Startup.");
            });
    

    } ```

  4. 添加一个名为ContactsController和继承自ApiController的新类文件,如下所示:

  5. Run the application by pressing F5 and go to http://localhost:55781/api/contacts in a browser, as shown in the following screenshot:

    Hosting OWIN in IIS and adding Web API to the OWIN pipeline

    图 6 - Contacts Web API 控制器返回 JSON 格式的联系人列表

  6. 请注意我们已经成功地将 Web API 添加到 OWIN 管道中,并且运行了它。

个人用户账号认证流程

Web API 中的个人用户登录使用 OAuth2 来使用资源所有者密码流对请求进行认证。 资源所有者密码流是 OAuth2 中定义的一个大类型。 此认证流使客户机能够向授权服务器发送用户名和密码。 本地登录的基本流程如下所示:

  1. 最终用户在客户端屏幕上提供用户名和密码。
  2. 客户端将用户名和密码发送给服务器,服务器返回一个访问令牌。
  3. 服务器验证接收到的用户名和密码,并返回一个访问令牌。
  4. 客户端通过在Authorization报头中发送 HTTP 请求并发送访问令牌来访问受保护的资源。

下图展示了 OAuth2 认证的基本流程:

Individual User Account authentication flow

图 7 -资源所有者密码流程

下图显示了在 ASP 中认证过程。 净 Web API。 Web API 中的授权服务器负责对请求进行认证并发出令牌。 在这里,资源服务器是 Web API 控制器。 因此,为了保护 Web API 控制器免受未经授权的访问,我们需要用[Authorize]属性装饰这些控制器。 如果控制器或动作带有[Authorize]属性,则必须对请求进行认证。 如果请求没有经过认证,那么 Web API 将返回 401 (Unauthorized)错误,因为授权被拒绝了:

Individual User Account authentication flow

图 8 - Web API 认证流程

发送未经授权的请求

为了查看向 Web API 控制器发送未经授权的请求的结果,该控制器被装饰为带有 Authorize 属性,让我们按照给定的步骤创建一个 Web API:

  1. 在 Visual Studio 的Start页面中创建新项目
  2. 选择 Visual c# Installed Template 命名为Web
  3. 选择ASP.NET Web 应用
  4. Name the project WebAPIWithAuthorize and click OK:

    Sending an unauthorized request

    图 9 -我们已经命名了 ASP.NET Web 应用作为“WebAPIWithAuthorize”

  5. 中选择Web API模板.NET 项目对话框。 这将在Add folders and core references下默认选择Web APIMVC

  6. Click OK to create the application, leaving Authentication as Individual User Accounts by default:

    Sending an unauthorized request

    图 10 -我们选择了 Web API

所创建的 Web API 项目包含一个名为HomeController的 MVC 控制器和两个 Web API 控制器,即AccountControllerValuesControllerAccountController处理用户账号的注册、登录、注销、密码修改等操作。 是一个具有一些基本操作的 Web API 控制器,例如插图。 您会注意到这个控制器是用[Authorize]属性装饰的。 所以只有经过认证的请求才能访问这个控制器:

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

让我们运行应用并浏览 URLhttp://localhost:61486/api/Values/。 您将得到以下授权错误:

Sending an unauthorized request

图 11 -发生非法访问错误

我还为您使用 Fiddler 捕获了这个操作的请求和响应,如下所示:

GET http://localhost:61486/api/Values HTTP/1.1
Host: localhost:61486

我收到的回复如下:

HTTP/1.1 401 Unauthorized
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
WWW-Authenticate: Bearer
X-Powered-By: ASP.NET
Date: Sun, 09 Aug 2015 09:14:21 GMT
Content-Length: 61

{"Message":"Authorization has been denied for this request."}

正如您在响应正文中收到的消息中所看到的,请求已被拒绝,因为它没有经过认证,并且响应返回 401 HTTP 状态码。 发生这种情况是因为请求中缺少 Authorization 头,因为没有承载令牌。

获取访问令牌

要将经过验证的请求发送给ValuesController,我们需要在 Authorization 报头中传递一个访问令牌。 我们如何得到这个访问令牌? 要获得这个访问令牌,我们需要登录应用。

让我们首先通过在 Account 控制器的 Web API register 操作方法中发布一个用户实例来注册一个用户,如下所示:

Get an access token

图 12 -发布一个 User 实例到 Web API

现在,我们有一个注册用户,用户名steve@jobs.com和密码Password@1。 让我们将请求体中 URL 编码的数据发送给令牌端点,如下所示:

{
  "grant_type": "password",
  "username": "steve@jobs.com",
  "Password": "Password@1"  
}

让我们使用 Fiddler 将这个实例发送到Token端点,如下所示:

Get an access token

图 13 -将具有授权类型的 User 实例发布到令牌端点

我们将在 Set-Cookie 报头中收到一个带有令牌的响应,如下图所示:

Get an access token

图 14 - Set-Cookie 报头中的 Token 响应

您也可以使用 AJAX post 请求或 c#发送此数据。 为了演示如何使用 c#获取令牌,我们在HomeController中引入了一个名为GetToken的操作和一个用于此操作的视图。 GetToken的 c#代码如下:

public ActionResult GetToken()
{
    var body = new List<KeyValuePair<string, string>>
    {
        new KeyValuePair<string, string>( "grant_type", "password" ), 
        new KeyValuePair<string, string>( "username", "test@test.com" ), 
        new KeyValuePair<string, string> ( "Password", "Sample@1" )
    };
    var content = new FormUrlEncodedContent(body);

    using (var client = new HttpClient())
    {
        var response = client.PostAsync("http://localhost:61486/Token", content).Result;
        var result = response.Content.ReadAsStringAsync().Result;

        ViewBag.token = JsonConvert.DeserializeObject<Dictionary<string, string>>(result)["access_token"];
    }
    return View();
}

我们构造带有用户名和密码以及授权类型的请求体。 然后,我们将内容发布到 URLhttp://localhost:61486/Token/。 最后,我们对响应内容进行反序列化,并将访问令牌分配给 ViewBag 命名的令牌。 在GetToken视图中,我们打印分配给视图袋的访问令牌,如下图所示:

Get an access token

图 15 -视图上打印出检索到的访问令牌

发送经过验证的请求

在前面的部分中,我们实现了如何通过usernamepassword从服务器检索访问令牌。 现在,在接收到的访问令牌的帮助下,让我们发送经过认证的请求。 经过认证的请求将在请求中有一个 Authorization 头。 我们通过 Fiddler 发送请求。 请求和响应如下截图所示:

Send an authenticated request

图 16 -发送带有承载令牌的授权请求

我们也可以在 c#代码中使用HttpClient在头文件中发送Bearer令牌,如下所示:

using (var client = new HttpClient())
{
       var token = "access_token";
       client.DefaultRequestHeaders.Add("Authorization", String.Concat("Bearer ", token));
       var response = client.GetAsync("http://localhost:61486/api/Values").Result;
       var values = response.Content.ReadAsStringAsync().Result;
}

提示

注意:总是建议为 Web API 设置 SSL,以促进这种类型的认证机制。

总结

华友世纪! 我们刚刚使用基于令牌的认证来保护我们的 Web API。

在本章中,您学习了如何在 IIS 中托管 OWIN,以及如何将 Web API 添加到 OWIN 管道中。 您还学习并理解了本地登录认证的流程。

我们演练了如何发送未经授权的请求,并了解了其影响。

然后,我们发送用户名和密码以获得访问令牌,以便进一步访问需要经过认证的请求的资源。

最后,您看到了如何在请求的 Authorization 头中发送承载令牌并访问受保护的资源。

在下一章中,让我们通过使用认证过滤器启用基本认证来保护 Web API。

让我们把安全措施提高到更高一级!