ASP-NET-Web-API-安全基础知识-全-
ASP.NET Web API 安全基础知识(全)
零、前言
ASP.NET Web API 是一个框架,它使构建 HTTP 服务变得很容易,这些服务可以到达广泛的客户机,包括浏览器和移动设备。 它是在。net 框架上构建 RESTful 应用的理想平台。
这本书提供了一个实用指南,以确保您的 ASP。 asp.net Web API 通过各种安全技术,如集成 asp.net.NET 身份认证系统,实现多种认证机制,启用安全套接字层(SSL),防止跨站点伪造请求攻击,实现跨源资源共享。
这本书的内容
第 1 章,设置浏览器客户端,帮助您设置浏览器客户端以使用 Web API 服务。 它还涵盖了 ASP.NET Web API 安全架构、认证和授权,以保护 Web API 不受未授权用户的影响。
第二章、在 ASP 中启用 SSL asp.net Web API,说明了如何使用 SSL 与 ASP.NET Web API,包括使用 SSL 客户端证书。 有几种常见的认证方案不能通过普通 HTTP 进行保护,特别是基本认证和表单认证,它们发送未加密的凭据。 为了安全,这些认证方案必须使用 SSL。 除此之外,还可以使用 SSL 客户机证书对客户机进行认证。
第三章,集成 ASP。 asp.net 身份识别系统。 asp.net Web API,说明了如何集成 asp.net。 asp.net 身份识别系统。 净 Web API。 ASP.NET 身份识别系统的设计是为了取代以前的 asp.net.NET 会员和简单会员系统。 它包括概要文件支持和 OAuth 集成。 它的工作与 OWIN 和包括与 ASP.NET 模板是 Visual Studio 2013 及其后续版本附带的。
第四章,使用 OAuth2 保护 web API,展示了如何使用 OAuth2 保护 web API 通过 OWIN 中间件对成员数据库进行认证。 您将能够使用本地登录使用 OAuth2 发送经过认证的请求。
第五章,在 Web API 中使用认证过滤器启用基本认证,介绍了如何使用认证过滤器为单个控制器或动作设置认证方案。 本章展示了一个实现 HTTP 基本访问认证方案的认证过滤器。 它还将介绍使用基本认证的优点和缺点。
第六章,确保 Web API 使用形式和 Windows 认证,解释了如何使用表单认证安全的 Web API 以及如何与他们的 Windows 用户可以登录凭证使用 Windows 集成验证。 您还将了解在 Web API 中使用窗体和 Windows 认证的优点和缺点。 表单认证使用 HTML 表单将用户凭证发送到服务器。 集成的 Windows 认证允许用户使用 Kerberos 或 NTLM 使用其 Windows 凭证登录。 客户端在 Authorization 报头中发送凭证。 Windows 认证最适合内网环境。
第 7 章,使用 ASP 使用外部认证服务 NET Web API,帮助您理解为了启用 OAuth/OpenID 和社交媒体认证而需要外部认证服务。 在创建新的 web 应用时,使用外部认证服务有助于减少开发时间。 网络用户通常拥有多个流行网络服务和社交媒体网站的现有账户; 因此,当一个 web 应用从外部 web 服务或社交媒体网站实现认证服务时,它节省了原本在创建认证实现时所花费的开发时间。 使用外部认证服务可以使最终用户不必为 web 应用创建另一个帐户,也不必记住另一个用户名和密码。
第八章,防止 Web API 中的跨站点请求伪造(CSRF)攻击,帮助您在 ASP 中实现反 CSRF 的措施。 净 Web API。 使用基于 API 密钥的认证或更复杂的机制(如 OAuth)有助于防止 CSRF 攻击。 ASP.NET MVC 使用防伪令牌,也称为请求验证令牌。
第 9 章、在 ASP 中启用跨源资源共享(CORS) NET Web API,解释如何在您的 Web API 应用中启用 CORS。 浏览器安全性可以防止网页向另一个域发出 AJAX 请求。 此限制称为同源策略,用于防止恶意站点从另一个站点读取敏感数据。 然而,有时你可能想让其他网站调用你的 web API。
你需要什么来写这本书
开发软件要求:
- Visual Studio 2013 或更高版本
- Windows 7 及以上版本
开发硬件要求:
- 1.6 GHz 或更快的处理器
- 1gb RAM(如果在虚拟机上运行,则为 1.5 GB)
- 10gb (NTFS)可用硬盘空间
- 5400 转硬盘
- DirectX 9-capable 显卡运行在 1024 x 768 或更高的显示分辨率
这本书是写给谁的
这本书是为每个拥有开发 ASP 知识的人准备的.NET Web API 应用。 良好的 c#和。net 框架的工作知识和经验是学习本书的先决条件。
约定
在这本书中,你会发现许多不同的文本样式来区分不同种类的信息。 下面是这些风格的一些例子,以及对它们含义的解释。
文本中的代码字、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 url、用户输入和 Twitter 句柄如下所示:“我们可以通过使用include指令来包含其他上下文。”
一段代码设置如下:
[default]
exten => s,1,Dial(Zap/1|30)
exten => s,2,Voicemail(u100)
exten => s,102,Voicemail(b100)
exten => i,1,Voicemail(s0)
当我们希望提请您注意代码块的特定部分时,相关的行或项以粗体显示:
[default]
exten => s,1,Dial(Zap/1|30)
exten => s,2,Voicemail(u100)
exten => s,102,Voicemail(b100)
exten => i,1,Voicemail(s0)
任何命令行输入或输出都写如下:
# cp /usr/src/asterisk-addons/configs/cdr_mysql.conf.sample
/etc/asterisk/cdr_mysql.conf
新词语、重要词语以粗体显示。 您在屏幕上看到的单词,例如,在菜单或对话框中,会出现如下文本:“单击Next按钮将您移动到下一个屏幕。”
注意事项
警告或重要说明显示在这样的框中。
提示
提示和技巧是这样的。
读者反馈
我们欢迎读者的反馈。 让我们知道你对这本书的看法——你喜欢或不喜欢这本书。 读者反馈对我们来说很重要,因为它能帮助我们开发出你能真正从中获益最多的游戏。
要向我们发送一般性的反馈,只需发送电子邮件<[feedback@packtpub.com](mailto:feedback@packtpub.com)>,并在邮件主题中提到这本书的标题。
如果有一个主题,你有专业知识,你有兴趣写或贡献一本书,请参阅我们的作者指南www.packtpub.com/authors。
客户支持
现在,你已经自豪地拥有了一本书,我们有一些东西可以帮助你从购买中获得最大的好处。
示例代码下载
您可以从您的帐户http://www.packtpub.com下载您购买的所有 Packt Publishing 图书的示例代码文件。 如果您在其他地方购买这本书,您可以访问http://www.packtpub.com/support并注册,将文件直接通过电子邮件发送给您。
勘误表
尽管我们已经竭尽全力确保内容的准确性,但错误还是会发生。 如果你在我们的书中发现错误,也许是文本或代码上的错误,如果你能向我们报告,我们将不胜感激。 通过这样做,您可以使其他读者免受挫折,并帮助我们改进这本书的后续版本。 如果您发现任何勘误表,请访问http://www.packtpub.com/submit-errata,选择您的图书,点击勘误表提交表格链接,并输入您的勘误表详细信息。 一旦您的勘误表被核实,您的提交将被接受,勘误表将被上载到我们的网站或添加到该标题的勘误表部分下的任何现有勘误表列表中。
要查看之前提交的勘误表,请访问https://www.packtpub.com/books/content/support并在搜索字段中输入书名。 所需资料将出现在勘误表部分。
盗版
在互联网上盗版受版权保护的材料是一个贯穿所有媒体的持续问题。 在 Packt,我们非常重视版权和授权的保护。 如果您在互联网上发现我们的作品以任何形式的非法拷贝,请立即提供我们的地址或网站名称,以便我们进行补救。
请通过<[copyright@packtpub.com](mailto:copyright@packtpub.com)>与我们联系,并提供疑似盗版资料的链接。
我们感谢您的帮助,保护我们的作者和我们的能力,为您带来有价值的内容。
问题
如果您对本书的任何方面有任何疑问,您可以通过<[questions@packtpub.com](mailto:questions@packtpub.com)>与我们联系,我们将尽力解决问题。
一、设置浏览器客户端
如果你在读这本书,那是因为你理解保护你的 web API 的重要性。 ASP.NET Web API 是一个框架,它帮助构建可以被广泛的客户端利用的 HTTP 服务。 所以确保 Web API 的安全是非常重要的。
ASP.NET Web API 1.0 没有任何安全特性,所以安全是由主机(如 Internet Information Server)提供的。 在 ASP.NET Web API 2,安全特性如 Katana 被引入。 为了保护 Web API,让我们了解所涉及的各种技术,并选择正确的方法。
在本章中,我们将涵盖以下主题:
- ASP.NET Web API 安全体系结构
- 设置浏览器客户端
- 认证和授权
- 在 HTTP 消息处理程序中实现认证
- 设置主
- 使用[Authorize]属性
- 自定义授权过滤器
- 控制器操作中的授权
NET Web API 安全体系结构
本节将向您概述 Web API 安全体系结构,并向您展示可用于安全相关事务的所有各种扩展点。 ASP.NET Web API 安全体系结构由三个主要层组成。 宿主层充当 Web API 和网络堆栈之间的接口。 消息处理程序管道层支持实现诸如认证和缓存等横切关注点。 控制器处理层是执行控制器和操作、绑定和验证参数以及创建 HTTP 响应消息的地方。 该层还包含一个过滤器管道,如下图所示:

图 1 -此图像显示了保护 Web API 所涉及的组件
让我们简要讨论一下 Web API 管道中每个组件的用途,如下所示:
- Open Web Interface for . net(OWIN)是新的开放标准托管基础设施。 微软已经在 OWIN 和的基础上构建了自己的框架 Katana,所有的 Web API 安全技术,如认证方法(例如,基于令牌的认证)和对社交登录提供商(例如,谷歌和 Facebook)的支持都将在 OWIN 层实现。
- 消息处理程序是一个类,用于接收 HTTP 请求并返回 HTTP 响应。 不建议在消息处理程序级别实现认证。 消息处理器用于跨源资源共享(CORS)。
- 认证过滤器保证在授权过滤器之前运行。 如果您对在 OWIN 层操作认证逻辑不感兴趣,您可以直接转移到控制器或操作。 认证过滤器对于调用基于 owen 的认证逻辑非常有用。
- 授权过滤器是管道中的位置,您可以在实际昂贵的业务逻辑在模型绑定和验证中运行以及控制器动作被调用之前重新检查请求。
现在我们已经熟悉了安全体系结构,接下来我们将设置客户机。
设置浏览器客户端
让我们创建一个用于联系人查找的 Web API。 这个联系人查找 Web API 服务将向调用的客户端应用返回联系人列表。 然后,我们将使用 jQuery AJAX 调用来列出和搜索联系人,使用 Contact Lookup 服务。
这个应用将帮助我们在本书中演示 Web API 安全性。
实现 Web API 查找服务
在这个节中,我们将创建一个联系人查找 web API 服务,该服务返回一个联系人列表,该列表采用JavaScript 对象标记(JSON)格式。 使用这个联系人查找的客户端是一个简单的 web 页面,它使用 jQuery 显示联系人列表。 按照以下步骤启动项目:
-
在 Visual Studio 的Start页面中创建新项目。
-
选择 Visual c# Installed Template 命名为Web。
-
选择ASP.NET Web 应用。
-
Name the project
ContactLookupand click OK, as shown in the following screenshot:![Implementing Web API lookup service]()
图 2 -我们已经命名了 ASP.NET Web 应用“ContactLookup”
-
在新 ASP. net 中选择空模板。 对话框。
-
Check Web API and click OK under Add folders and core references, as shown in the following:
![Implementing Web API lookup service]()
图 3 -我们选择空 Web API 模板
我们只是创建了一个空的 Web API 项目。 现在让我们添加所需的模型。
添加模型
让我们从开始,通过以下步骤的帮助创建一个代表联系人的简单模型:
-
First, define a simple contact model by adding a class file to the Models folder.
![Adding a model]()
图 4 -右键单击 Models 文件夹并添加一个 Model 类
-
命名类文件
Contact并声明Contact类的属性。namespace ContactLookup.Models { public class Contact { public int Id { get; set; } public string Name { get; set; } public string Email { get; set; } public string Mobile { get; set; } } }
我们刚刚添加了一个名为Contact的模型。 现在让我们添加所需的 web API 控制器。
添加控制器
HTTP 请求是由 Web API 中的控制器对象处理的。 让我们定义一个带有两个动作方法的控制器。 一个操作返回联系人列表,另一个操作返回特定于给定 ID 的单个联系人:
-
Add the Controller under the Controllers folder in Solution Explorer.
![Adding a controller]()
图 5 -右键单击 Controllers 文件夹并添加控制器
-
Select Web API Controller – Empty and click on Add in the Add Scaffold dialog.
![Adding a controller]()
图 6 -选择一个空的 Web API 控制器
-
Let's name the controller
ContactsControllerin the Add Controller dialog box and click Add.![Adding a controller]()
图 7 -命名控制器
这将在Controllers文件夹中创建
ContactsController.cs文件,如下图所示:![Adding a controller]()
图 8 -应用中的 Controllers 文件夹中添加了 ContactsController
-
用以下代码替换
ContactsController中的代码:namespace ContactLookup.Controllers { public class ContactsController : ApiController { Contact[] contacts = new Contact[] { new Contact { Id = 1, Name = "Steve", Email = "steve@gmail.com", Mobile = "+1(234)35434" }, new Contact { Id = 2, Name = "Matt", Email = "matt@gmail.com", Mobile = "+1(234)5654" }, new Contact { Id = 3, Name = "Mark", Email = "mark@gmail.com", Mobile = "+1(234)56789" } }; public IEnumerable<Contact> GetAllContacts() { return contacts; } public IHttpActionResult GetContact(int id) { var contact = contacts.FirstOrDefault(x => x.Id == id); if (contact == null) { return NotFound(); } return Ok(contact); } } }
为简单起见,联系人存储在控制器类中的固定数组中。 控制器定义为,有两个动作方法。 联系人列表将由GetAllContacts方法以 JSON 格式返回,GetContact方法根据其 ID 返回单个联系人。 一个惟一的 URI 应用于控制器上的每个方法,如下表所示:
|控制器方法
|
尤里
|
| --- | --- |
| GetAllContacts | /api/contacts |
| GetContact | /api/contacts/id |
使用 JavaScript 和 jQuery 使用 Web API
在这个节中,为了演示使用或不使用任何安全机制调用 web API,让我们创建一个 HTML 页面,使用 web API 并使用 jQuery AJAX 调用的结果更新页面:
-
In the Solution Explorer pane, right-click on the project and add New Item.
![Consuming the Web API using JavaScript and jQuery]()
图 9 -从解决方案资源管理器的上下文菜单中选择 add new item
-
Create HTML Page named
index.htmlusing the Add New Item dialog.![Consuming the Web API using JavaScript and jQuery]()
图 10 -添加索引 html 文件通过选择 html 页面在添加新项目对话框
-
用以下代码替换
index.html文件中的内容:<!DOCTYPE html> <html > <head> <title>Contact Lookup</title> </head> <body> <div> <h2>All Contacts</h2> <ul id="contacts" /> </div> <div> <h2>Search by ID</h2> <input type="text" id="contactId" size="5" /> <input type="button" value="Search" onclick="search();" /> <p id="contact" /> </div> <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.3.min.js"></script> <script> var uri = 'api/contacts'; $(document).ready(function () { // Send an AJAX request $.getJSON(uri) .done(function (data) { // On success, 'data' contains a list of contacts. $.each(data, function (key, contact) { // Add a list item for the contact. $('<li>', { text: formatItem(contact) }).appendTo($('#contacts')); }); }); }); function formatItem(contact) { return contact.Name + ', email: ' + contact.Email + ', mobile: ' + contact.Mobile; } function search() { var id = $('#contactId').val(); $.getJSON(uri + '/' + id) .done(function (data) { $('#contact').text(formatItem(data)); }) .fail(function (jqXHR, textStatus, err) { $('#contact').text('Error: ' + err); }); } </script> </body> </html>
获取联系人列表
我们需要向/api/contacts 发送一个 HTTP GET 请求来获取联系人列表。 AJAX 请求由 jQuerygetJSON函数发送,并在响应中接收 JSON 对象数组。 如果请求成功,将调用done函数中的回调函数。 在回调中,我们用联系信息更新 DOM,如下所示:
$(document).ready(function () {
// Send an AJAX request
$.getJSON(uri)
.done(function (data) {
// On success, 'data' variable contains a list of contacts.
$.each(data, function (key, contact) {
// Add a list item for the contact.
$('<li>', { text: formatItem(contact) }).appendTo($('#contacts'));
});
});
});
通过 ID 获取联系人
要通过 ID 获取联系人,向/api/contacts/id发送一个 HTTP get 请求,其中id是联系人 ID。
function search() {
var id = $('#contactId').val();
$.getJSON(uri + '/' + id)
.done(function (data) {
$('#contact').text(formatItem(data));
})
.fail(function (jqXHR, textStatus, err) {
$('#contact').text('Error: ' + err);
});
}
getJSON中的请求 URL 有联系人 ID。 响应是此请求的单个联系人的 JSON 表示。
运行应用
按F5启动调试应用。 输入联系人 ID,点击搜索:

图 11 -样例基于浏览器的客户端应用的用户界面
认证授权
我们创建了一个简单的 web API,它根据 ID 返回联系人列表或特定的联系人。 任何支持 HTTP 的客户端都可以访问这个 web API,但安全性还不够。 在认证和授权机制的帮助下,我们可以保护这个 web API 免受未经授权的访问。
- 认证机制用于识别合法用户,并使用用户的身份进行认证。 在这里,身份可以是用户名和密码。
- 授权机制有助于限制对某个动作的未授权访问。 例如,未经授权的用户可以获取联系人列表。 但他只能建立新的联系。
认证
认证是在主机Internet Information Service(IIS)中对 web API 进行认证。 Internet 信息服务使用 HTTP 模块进行认证。 我们还可以用自己的 HTTP 模块实现自定义认证。
主机在对用户进行认证时创建主体。 Principal 是一个IPrincipal对象,它表示运行代码的安全上下文。 您可以从主机连接的Thread.CurrentPrincipal中访问当前的主体。 可以从 principal 的Identity对象访问用户信息。 如果用户通过认证,Identity.IsAuthenticated属性返回true。 如果用户没有被验证,Identity.IsAuthenticated将返回false。
授权
授权在向控制器提供成功的认证之后发生。 当做出更细粒度的选择时,它帮助您授予对资源的访问权。
对于任何未经授权的请求,授权筛选器将返回一个错误响应,并且不允许执行该操作。 这是因为授权过滤器将在控制器操作中的任何语句之前首先执行。
实现 HTTP 消息处理程序中的认证
对于一个自托管 web API,最佳的实践是在HTTP 消息处理程序中实现认证。 主体将在验证 HTTP 请求后由消息处理程序设置。 对于自托管的 web API,可以考虑在消息处理程序中实现认证。 否则,请使用 HTTP 模块。
下面的代码片段展示了一个在 HTTP 模块中实现的基本认证示例:
public class AuthenticationHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
var credentials = ParseAuthorizationHeader(request);
if (credentials != null)
{
// Check if the username and passowrd in credentials are valid against the ASP.NET membership.
// If valid, the set the current principal in the request context
var identity = new GenericIdentity(credentials.Username);
Thread.CurrentPrincipal = new GenericPrincipal(identity, null);;
}
return base.SendAsync(request, cancellationToken)
.ContinueWith(task =>
{
var response = task.Result;
if (credentials == null && response.StatusCode == HttpStatusCode.Unauthorized)
Challenge(request, response);
return response;
});
}
protected virtual Credentials ParseAuthorizationHeader(HttpRequestMessage request)
{
string authorizationHeader = null;
var authorization = request.Headers.Authorization;
if (authorization != null && authorization.Scheme == "Basic")
authorizationHeader = authorization.Parameter;
if (string.IsNullOrEmpty(authorizationHeader))
return null;
authorizationHeader = Encoding.Default.GetString(Convert.FromBase64String(authorizationHeader));
var authenticationTokens = authorizationHeader.Split(':');
if (authenticationTokens.Length < 2)
return null;
return new Credentials() { Username = authenticationTokens[0], Password = authenticationTokens[1], };
}
void Challenge(HttpRequestMessage request, HttpResponseMessage response)
{
response.Headers.Add("WWW-Authenticate", string.Format("Basic realm=\"{0}\"", request.RequestUri.DnsSafeHost));
}
public class Credentials
{
public string Username { get; set; }
public string Password { get; set; }
}
}
设置主体
如果应用实现了自定义认证逻辑,那么我们必须在两个地方设置主体:
Thread.CurrentPrincipal是。net 中设置线程主体的标准方法。HttpContext.Current.User是特定于 ASP.NET 的。
下面的代码显示了如何设置主体:
private void SetPrincipal(IPrincipal principal)
{
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
}
使用[Authorize]属性
AuthorizeAttribute将确保用户是通过验证还是未通过验证。 如果用户未通过认证,且不调用相应的操作,则返回 HTTP 状态码为 401 的未授权错误。 Web API 允许您以三种方式应用筛选器。 我们可以在全局级别、控制器级别或单个操作级别应用它们。
全局授权过滤器
为了对所有 Web API 控制器应用授权过滤器,我们需要将AuthorizeAttribute过滤器添加到Global.asax文件的全局过滤器列表中,如下所示:
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new AuthorizeAttribute());
}
控制器级授权过滤器
要为特定的控制器应用授权过滤器,我们需要用 filter 属性装饰控制器,如下代码所示:
// Require authorization for all actions on the controller.
[Authorize]
public class ContactsController : ApiController
{
public IEnumerable<Contact> GetAllContacts() { ... }
public IHttpActionResult GetContact(int id) { ... }
}
动作级别授权过滤器
要对特定的动作应用授权过滤器,我们需要将属性添加到 action 方法中,如下代码所示:
public class ContactsController : ApiController
{
public IEnumerable<Contact> GetAllContacts() { ... }
// Require authorization for a specific action.
[Authorize]
public IHttpActionResult GetContact(int id) { ... }
}
自定义授权过滤器
要实现自定义授权筛选器,我们需要创建派生AuthorizeAttribute、AuthorizationFilterAttribute或IAuthorizationFilter的类。
AuthorizeAttribute:根据当前用户和用户角色进行授权。AuthorizationFilterAttribute:应用同步授权逻辑,且可能不基于当前用户或角色。IAuthorizationFilter:AuthorizeAttribute和AuthorizationFilterAttribute都实现IAuthorizationFilter。 如果需要高级授权逻辑,则需要实现IAuthorizationFilter。
控制器动作中的授权
有时,可能需要在基于主体处理请求后更改行为。 在这种情况下,我们可以在控制器操作中实现授权。 例如,如果您想根据用户的角色操作响应,我们可以通过动作方法本身的ApiController.User属性验证登录的用户角色:
public HttpResponseMessage Get()
{
if (!User.IsInRole("Admin"))
{
// manipulate the response to eliminate information that shouldn't be shared with non admin users
}
}
总结
这很容易,不是吗? 我们刚为 APS 设置了安全系统.NET Web API,我们将在接下来的章节中构建它。
您学习了 ASP 的安全体系结构.NET Web API,它提供了一个整体的视图。 然后我们设置浏览器客户机,从实现 Web 查找服务到使用 JavaScript 和 jQuery 代码调用 Web API。
您还学习了认证和授权技术,我们将在本书后面详细介绍这些技术。 接下来,您了解了 HTTP 消息处理程序、Principal 和用于控制用户授权的[Authorize]属性。
最后,您了解了自定义授权和控制器操作中的授权,以便在基于主体处理请求后更改行为。
你在这一章学到了很多东西。 然而,这仅仅是个开始。 在下一章中,您将实现 Web API 的安全套接字层。 让我们开始吧!
二、为 ASP.NET Web API 启用 SSL
在本章中,我们将讨论使用 ASP 实现 SSL.NET Web API 和使用 SSL 客户端证书的用户认证。 基于普通 HTTP 的认证方案是不安全的。 例如,基本认证和表单认证发送纯文本,如用户名和密码。 因此,为了保护纯文本免受漏洞的侵害,我们使用 SSL,并使用 SSL 客户端证书对客户端进行认证。
在本章中,我们将涵盖以下主题:
- 在 Web API 控制器中强制使用 SSL
- 在 Web API 中使用客户机证书
在 Web API 控制器中强制 SSL
安全套接字层(SSL)加密保护客户端和服务器之间交换的凭据。 SSL 允许安全通道以加密格式传输认证消息。 128 位和 256 位 SSL 加密技术更加安全。 如果需要在客户端和服务器之间传输机密或个人数据,则需要使用 128 位或 256 位 SSL。 要解密使用 128 位或 256 位加密技术加密的内容是非常困难的。
SSL 和传输级别安全(TLS)使用公钥和对称密钥的组合加密。 SSL 握手是在服务器和客户机之间的初始通信期间交换消息的过程。 使用公钥技术允许服务器在握手过程中向客户机验证自己。
RSA 是一种密钥交换算法,它管理服务器和客户端在 SSL 握手期间确定要使用的对称密钥的方式。 SSL 加密套件使用 RSA 密钥交换,TLS 支持 ECC 加密套件和 RSA。
OpenSSH 基于 SSH 协议,它通过多种认证方法上的网络流量加密和提供安全隧道功能来帮助保护网络通信。 OpenSSH 是一种免费的开放源代码,可以用作未加密的网络通信协议(如 FTP 和 Rlogin)的替代品。
启用 SSL 允许客户端使用以HTTPS开头的 url 访问站点。 我们可以在 IIS 7 或更高版本中创建自签名证书; 它可以用于为站点启用 SSL 并添加 HTTPS 绑定。 为了开发目的,我们可以在 Visual Studio 中启用 IIS Express 中的 SSL,并在属性窗口中设置SSL Enabled为True,如下截图所示:

在某些情况下,我们可能希望同时支持 HTTPS 和 HTTP 绑定。 我们可以对某些资源或操作启用 HTTP,对其他资源或操作启用 SSL。 在这种情况下,我们可以将带有RequireHttps过滤器属性的操作添加到所需的 SSL。 下面的代码是一个验证 SSL 的 Web API 认证过滤器:
public class RequireHttpsAttribute : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
if (actionContext.Request.RequestUri.Scheme != Uri.UriSchemeHttps)
{
actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden)
{
ReasonPhrase = "HTTPS Required"
};
}
else
{
base.OnAuthorization(actionContext);
}
}
}
如下所示,您可以通过RequireHttps过滤器添加任何需要 SSL 的 Web API 动作:
public class ContactsController : ApiController
{
[RequireHttps]
public IEnumerable<Contact> GetAllContacts() { ... }
}

在 Web API 中使用客户端证书
为了以数字方式识别用户是否为经过认证的用户,需要使用客户端证书。 这是一种增强的安全机制,不像使用用户名和密码来保证安全性,因为这是一种简单的验证。 客户端证书允许 web 应用通过验证客户端在 HTTP 连接建立之前提供的证书来验证其用户。
创建 SSL 客户端证书
下面的步骤将创建一个用于开发和测试目的的测试根权威和客户端认证:
-
打开 Visual Studio 的开发人员命令提示符。
-
运行如下命令:
makecert.exe -n "CN=Dev CA" -r -sv DevCA.pvk DevCA.cer -
当提示
makecert输入私钥时,输入证书密码。
上述步骤有助于创建根证书颁发机构证书。 现在,让我们使用Microsoft Management Control(MMC)将DevCA.cer证书安装到本地机器存储的受信任根证书颁发机构上,步骤如下:
- 打开微软管理控制。
- 在File菜单中点击添加/删除管理单元。
- 从可用管理单元列表中选择证书,然后点击添加按钮。
- 从证书管理单元弹出窗口中选择计算机帐户管理证书,然后点击下一步按钮。
- 在选择计算机窗口中选择本地计算机,然后点击完成按钮。
- 现在点击OK返回到 MMC。
- 在控制台根树视图下,选择受信任的根证书颁发机构节点。
- 点击Actions窗格中的More Actions,并导航到All Tasks|Import导入证书。
- 浏览
DevCA.cer证书文件,并单击Open按钮。 - 单击Next,完成向导。
- 提示时重新输入密码。
- 在 Visual Studio 的开发者命令提示符中执行以下命令来创建已签名的客户端证书:
```
makecert.exe -pe -ss My -sr CurrentUser -a sha1 -sky exchange -n "CN=name" -eku 1.3.6.1.5.5.7.3.2 -sk SignedByCA -ic DevCA.cer -iv DevCA.pvk
```
配置 IIS 以接受客户端证书
下面的步骤将配置 IIS 以接受客户端证书:
- 在 IIS 管理器的树状视图中单击站点节点。
- 通过双击SSL 设置打开它。
- 在客户端证书中选择以下任一选项:
- Accept:IIS 将接受来自客户端的证书,但不需要
- Require:IIS 需要一个客户端证书(要启用此选项,您还必须选择Require SSL)
现在,您知道了如何在 IIS Express 中进行配置以接受或要求客户端证书。 接下来,我们将看到如何在 Web API 中使用客户机证书。
在 Web API 中验证客户端证书
启用 HTTPS 并在 SSL 中使用客户端证书,并将签名证书分发给用户,将保护 Web API 并对客户端进行认证。
在请求消息上调用GetClientCertificate将客户端证书返回给 Web API。 如果没有客户端证书,该方法返回 null。 如果存在客户端证书,则返回X509Certificate2实例。 我们可以从实例对象X509Certificate2中获取关于发行者和主题的详细信息,并可用于认证和授权:
X509Certificate2 cert = Request.GetClientCertificate();
string issuer = cert.Issuer;
string subject = cert.Subject;
总结
我们刚刚看到了如何在 ASP 中使用 SSL.NET Web API,学习了如何使用 SSL 实现基本认证和表单认证。
您还了解了如何创建客户机证书并配置 IIS 以接受它们。
你必须知道如何在 Web API 中使用客户端证书。
在下一章中,让我们集成 ASP。 asp.net 身份识别系统。 净 Web API。 ASP.NET 身份识别系统将帮助您实现应用的社交登录。 它还允许您存储用户配置信息,如出生日期。 ASP.NET 标识在 asp.net 中可用.NET MVC、Web 表单和 Web API 模板。
三、ASP.NET Web API 的身份识别系统
本章实际说明了如何集成 ASP。 asp.net 身份识别系统。 净 Web API。 ASP。 asp.net 身份识别系统是对 asp.net 的一个升级.NET 会员和简单会员系统。 它具有用户配置文件支持,OAuth 集成,并且在 ASP 中可用。 Visual Studio 2013/2015 的 asp.net 模板。
在本章中,我们将涵盖以下主题:
- 创建一个空的 Web API 应用
- 安装 ASP.NET Identity NuGet 包
- 设置 ASP。 2.1 网络身份
- 定义 Web API 控制器和方法
创建空 Web API 应用
让我们创建一个空的 Web API 来集成 ASP。 净的身份。 遵循以下步骤:
-
在 Visual Studio 的Start页面中创建新项目。
-
选择 Visual c# Installed Template 命名为Web。
-
选择ASP.NET Web 应用。
-
Name the project
ContactLookupWithAspNetIdentityand click on OK:![Creating an Empty Web API Application]()
图 1 -我们已经命名了 ASP.NET Web 应用作为 ContactLookupWithAspNetIdentity
-
在新 ASP. net 中,选择空模板.NET 项目对话框。
-
Check Web API and click OK under Add folders and core references:
![Creating an Empty Web API Application]()
图 2 -选择 ASP。 应用“ContactLookupWithAspNetIdentity”的 NET Web API 模板
创建的空 Web API 的解决方案生成以下文件:

图 3 -空 ASP 的解结构.NET Web API 应用“ContactLookupWithAspNetIdentity”
安装 ASP.NET Identity NuGet 包
我们创建了一个空的 Web API Web 应用。 现在我们需要安装所需的 NuGet 包来集成 ASP.NET 身份到 Web API。 让我们在 NuGet 包管理器控制台下执行以下语句来安装 ASP.NET Identity 2.1 包到创建的应用:
Install-Package Microsoft.AspNet.Identity.Core
Install-Package Microsoft.AspNet.Identity.EntityFramework
Install-Package Microsoft.AspNet.Identity.OWIN
Install-Package Microsoft.OWIN.Cors
Install-Package Microsoft.AspNet.WebApi.OWIN
Install-Package Microsoft.OWIN.Security.OAuth
我们也可以安装 ASP.NET Identity 2.1 使用基于 ui 的 Nuget 包管理器,如下截图所示:

图 4 -安装 ASP.NET Identity 2.0 相关包使用 NuGet 包管理器
建立 ASP。 2.1 网络身份
我们已经创建了一个空的 Web API,并为 ASP 安装了必要的 NuGet 包.NET Identity andOpen Web Interface for . NET(OWIN) 现在让我们添加所需的类文件来集成 ASP.NET 身份到 Web API。
净的身份
ASP.NET Identity 是微软提供的一个框架,它建立在 OWIN 中间件之上,用于管理 ASP 中的用户身份和成员资格.NET 应用,如 Web Forms、MVC 和 Web API。 我们将使用 ASP.NET 身份认证系统使用内置的身份用户域模型IdentityUser注册和管理身份用户。 如果您计划在您的应用中拥有额外的属性,那么您将需要创建一个具有额外属性的域模型IdentityUser。 让我们为用户定义一个自定义实体框架类,该类具有应用特定的属性,比如名和姓。 我们还需要定义一个 db context 来处理与数据库的通信:
-
Let's create a class named
ApplicationUserinheritingIdentityUserin theModelsfolder:public class ApplicationUser : IdentityUser { [Required] [MaxLength(50)] public string FirstName { get; set; } [Required] [MaxLength(50)] public string LastName { get; set; } }这个扩展标识用户类包含特定于应用的额外属性,例如
FirstName和LastName数据,用所需的和最大长度的验证属性进行注释。 -
Now, add a class named
ApplicationDbContextto theModelsfolder to manage communication with db:public class ApplicationDbContext : IdentityDbContext<ApplicationUser> { public ApplicationDbContext() : base("DefaultConnection", throwIfV1Schema: false) { Configuration.ProxyCreationEnabled = false; Configuration.LazyLoadingEnabled = false; } public static ApplicationDbContext Create() { return new ApplicationDbContext(); } }我们刚刚创建了一个继承了
IdentityDbContext的ApplicationDbContext类文件,它将有助于在 SQL Server 中管理特定于身份的表。 如您所见,我们将连接字符串名称作为参数传递给基构造函数,以便可以使用它来标识各自的服务器和数据库名称。 -
Let's now add the connection string to the
web.configfile that will point to the database, which will be created using code first approach:<connectionStrings> <add name="DefaultConnection" connectionString="(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\AspNetIdentity.mdf;Initial Catalog=AspNetIdentity;Integrated Security=True" providerName="System.Data.SqlClient" /> </connectionStrings>我们添加了所需的连接字符串。 现在,我们需要首先启用将生成代码的实体框架代码迁移,以便从域模型代码更新数据库模式。
-
要添加 NuGet 包进行迁移,请在 NuGet 包管理控制台执行以下命令:
enable-migrations add-migration AspNetDbCreate update-databaseenable-migrations命令在ContactLookupWithAspNetIdentity项目中创建Migrations文件夹,并生成一个名为Configuration的文件。 方法将运行或测试应用所需的基本数据添加到 db 中。add-migration AspNetDbCreate命令在Migrations文件夹中生成<timestamp>_AspNetDbCreate.cs代码以创建数据库。 如果您注意到,该文件还包含方法Up中的ApplicationUser类中扩展的特定于应用的数据属性的代码。update-database命令执行配置文件中的Up方法和Seed方法中的代码。
下图显示了添加所需文件并启用迁移后的解决方案结构:

图 5 -带 Db Migration 配置文件的“ContactLookupWithAspNetIdentity”解决方案结构
-
添加
UserManager类来管理ApplicationUser实例。 为此,让在App_Start中创建一个名为IdentityConfig.cs的文件作为标准,并为UserManager类添加以下代码:public class ApplicationUserManager : UserManager<ApplicationUser> { public ApplicationUserManager(IUserStore<ApplicationUser> store) : base(store) { } public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOWINContext context) { var appDbContext = context.Get<ApplicationDbContext>(); var appUserManager = new ApplicationUserManager(new UserStore<ApplicationUser>(appDbContext)); return appUserManager; } }
我们还可以在ApplicationUserManager类的 create 方法中添加代码,为用户名和密码配置验证逻辑,为电子邮件和 SMS 实现双因素认证提供者,并为 Web API 启用基于角色的授权。
下面的代码添加了验证用户概要文件的规则。 根据代码,用户名被限制只接受字母数字用户名(用户名应该以字母开头),电子邮件地址应该是有效的和强制性的:
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = true,
RequireUniqueEmail = true
};
下面的代码添加了验证密码的规则,以限制密码的最小和最大长度,并由至少一个特殊字符、数字和至少一个大写或小写字母组成:
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
ApplicationUserManager类中的静态Create方法将作为回调方法注册在Startup.Auth.cs中,该回调方法将被调用以创建ApplicationUserManager的实例。 该实例将存储在OWINContext中,可以通过调用context.Get方法获得:
添加以下代码的 OWIN 启动类到App_Start文件夹:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration httpConfig = new HttpConfiguration();
ConfigureOAuthTokenGeneration(app);
ConfigureWebApi(httpConfig);
app.UseCors(Microsoft.OWIN.Cors.CorsOptions.AllowAll);
app.UseWebApi(httpConfig);
}
private void ConfigureOAuthTokenGeneration(IAppBuilder app)
{
app.CreatePerOWINContext(ApplicationDbContext.Create);
app.CreatePerOWINContext<ApplicationUserManager>(ApplicationUserManager.Create);
}
private void ConfigureWebApi(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
}
这个Startup类为您提供了配置授权信息和 Web API HTTP、设置 CORS、JSON 类型格式化器等的可访问性的能力。
应用用户管理器实例在帐户控制器中通过注入构造函数或从OWINContext在 Request 对象中可用。 应用用户管理器的实例提供了获取身份用户信息、更改密码、为身份用户添加外部登录信息、删除身份用户的本地或外部登录、注册用户和添加登录信息的方法。
定义 Web API 控制器和方法
到目前为止,我们创建了一个空的 ASP.NET Web API 应用。 我们为 ASP 安装了必要的 NuGet 包.NET 身份和 OWIN 集成。 我们还为身份用户和 db 上下文添加了代码,并为实体框架代码首先迁移启用了 db 迁移。 现在让我们将添加所需的控制器和方法到管理应用标识系统中的用户帐户。
创建一个名为AccountsController的控制器,并添加以下代码:
namespace ContactLookupWithAspNetIdentity.Controllers
{
[RoutePrefix("api/accounts")]
public class AccountsController : ApiController
{
public ApplicationUserManager UserManager
{
get
{
return Request.GetOWINContext().GetUserManager<ApplicationUserManager>();
}
}
[Route("user/{id:guid}")]
public async Task<IHttpActionResult> GetUserById (string Id)
{
var user = await UserManager.FindByIdAsync(Id);
if (user != null)
{
return Json(user);
}
return NotFound();
}
[Route("user/{username}")]
public async Task<IHttpActionResult> GetUserByName(string username)
{
var user = await UserManager.FindByNameAsync(username);
if (user != null)
{
return Json(user);
}
return NotFound();
}
[HttpPost]
[Route("Register")]
public async Task<IHttpActionResult> Register([FromBody] UserViewModel userVM)
{
var user = new ApplicationUser() {
UserName = userVM.UserName,
Email = userVM.Email,
FirstName = userVM.FirstName,
LastName = userVM.LastName
};
IdentityResult result = await UserManager.CreateAsync(user, "password@1");
if (!result.Succeeded)
{
return InternalServerError();
}
return Ok();
}
}
}
AccountsController中有三种方法,即GetUserById、GetUserByName、GetRegister。 注意,Register方法被实现为HttpPost以接受 POST 主体的用户实例,并将此身份用户插入到数据库中。 GetUserById和GetUserByName方法将返回与相应 ID 或名称匹配的用户信息。
测试应用
我们刚刚创建了所需的控制器和方法,以便为ContactLookup这样的应用管理身份用户。 现在,让我们测试AccountsController以验证已实现的插入身份用户并基于其 ID 或名称获取身份用户的方法是否正常工作。
让我们首先使用Fiddler工具调用Register方法,如下截图所示:

图 6 - POST 用户实例到 ASP.NET Web API 注册方法在帐户控制器
正如你可以看到的,我们通过Fiddler使用POST动作将用户实例发布到 Web APIRegister方法:

图 7 - 200 OK 响应,表示已创建用户
Web API 返回一个带有 HTTP 状态码200 OK的响应。 这意味着新的身份用户已经创建并插入到身份数据库中,如下截图所示:

图 8 -将注册身份用户添加到数据库中
现在,让我们尝试通过将用户 ID (GUID)传递给 Web API 操作方法来获取插入的用户详细信息,如下面的截图所示:

图 9 -对传递用户 ID 的 User 方法应用 GET 操作
如您所见,我们将GET操作应用于附加用户插入的标识的 GUID 的 URL,然后我们收到了匹配用户的 XML/JSON 格式的用户详细信息。
现在,让我们通过将用户名传递给 Web API 操作方法来获取插入的用户详细信息,如下面的截图所示:

图 10 -对传递 username 的 User 方法应用 GET 动作
类似地,我们通过应用GET方法将身份用户的用户名附加到 URL 中。 响应包含 XML/JSON 格式的匹配用户详细信息。
现在,Web API 已经准备好了.NET 身份识别系统集成。 因此,利用 ASP 提供的内置功能可以方便地管理身份用户。 净的身份系统。 接下来,您可以添加LookupController和必要的方法来返回联系人的详细信息。
总结
这很容易,不是吗? 我们只是集成了 APS.NET 身份,以管理身份用户和成员。
您学习了如何一步一步地创建一个空 ASP.NET Web API 项目,用于集成 asp.net。 净的身份。 然后我们安装了必要的 ASP.NET Identity NuGet 包作为集成过程的一部分。
您还学习了如何定义控制器和方法,以便管理应用的身份用户和成员关系。
最后,您学习了如何测试 ASP.NET 身份集成与一个空的 Web API 创建。 现在,我们有了必要的控制器和方法来管理ContactLookup的应用用户。 接下来,您可以创建其他需要的控制器来启用搜索ContactLookup服务的可用联系人。
你学习了 ASP.NET 身份集成到 Web API。 在下一章中,您将使用 OAuth2 保护 Web API。
把那些安全墙竖起来!
四、使用 OAuth2 保护 Web API
本章解释了如何使用 OAuth2 来保护一个 web API,通过使用 OWIN 中间件对成员数据库进行认证。 您将能够使用本地登录使用 OAuth2 发送经过认证的请求。
在本章中,我们将涵盖以下主题:
- 在 IIS 中托管 OWIN 并将 Web API 添加到 OWIN 管道中
- 个人用户帐户登录认证流程
- 发送未经授权的请求
- 获取访问令牌
- 发送经过验证的请求
在 IIS 中托管 OWIN,并向 OWIN 管道中添加 Web API
让我们创建一个空的 Web API 模板来集成 ASP。 净的身份。 遵循以下步骤:
-
在 Visual Studio 的Start页面中创建新项目。
-
选择 Visual c# Installed Template 命名为Web。
-
选择ASP.NET Web 应用。
-
Name the project
ContactLookupOwinand click OK:![Hosting OWIN in IIS and adding Web API to the OWIN pipeline]()
图 1 -我们已经命名了 ASP.NET Web 应用作为“ContactLookupOwin”
-
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”选择空模板
-
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”
-
Right-click on Project and select Add New Item in the
ContactLookupOwinproject. Then, select the OWIN startup class in the center pane and name the class fileOwinStartup.cs:![Hosting OWIN in IIS and adding Web API to the OWIN pipeline]()
图 4 -添加了名为“OwinStartup.cs”的 OWIN 启动类文件
-
用以下代码替换
OwinStartup类中的Configuration方法:
这段代码配置响应的内容类型,写入响应体。 当服务器接收到 HTTP 请求时,OWIN 管道会调用中间件。
通过按F5运行应用,您将在浏览器中看到以下输出:

图 5 -响应注入内容“Owin Startup”
按照给出的步骤将 Web API 添加到 OWIN 管道:
-
在包管理控制台运行如下命令安装 NuGet 包
Microsoft.AspNet.WebApi.OwinSelfHost:Install-Package Microsoft.AspNet.WebApi.OwinSelfHost -
这个包使应用能够托管 ASP.NET Web API 在我们的进程中使用 OWIN HttpListener 服务器。
-
修改
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."); }); } -
添加一个名为
ContactsController和继承自ApiController的新类文件,如下所示: -
Run the application by pressing F5 and go to
http://localhost:55781/api/contactsin 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 格式的联系人列表
-
请注意我们已经成功地将 Web API 添加到 OWIN 管道中,并且运行了它。
个人用户账号认证流程
Web API 中的个人用户登录使用 OAuth2 来使用资源所有者密码流对请求进行认证。 资源所有者密码流是 OAuth2 中定义的一个大类型。 此认证流使客户机能够向授权服务器发送用户名和密码。 本地登录的基本流程如下所示:
- 最终用户在客户端屏幕上提供用户名和密码。
- 客户端将用户名和密码发送给服务器,服务器返回一个访问令牌。
- 服务器验证接收到的用户名和密码,并返回一个访问令牌。
- 客户端通过在Authorization报头中发送 HTTP 请求并发送访问令牌来访问受保护的资源。
下图展示了 OAuth2 认证的基本流程:

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

图 8 - Web API 认证流程
发送未经授权的请求
为了查看向 Web API 控制器发送未经授权的请求的结果,该控制器被装饰为带有 Authorize 属性,让我们按照给定的步骤创建一个 Web API:
-
在 Visual Studio 的Start页面中创建新项目。
-
选择 Visual c# Installed Template 命名为Web。
-
选择ASP.NET Web 应用。
-
Name the project
WebAPIWithAuthorizeand click OK:![Sending an unauthorized request]()
图 9 -我们已经命名了 ASP.NET Web 应用作为“WebAPIWithAuthorize”
-
在中选择Web API模板.NET 项目对话框。 这将在Add folders and core references下默认选择Web API和MVC。
-
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 控制器,即AccountController和ValuesController。 AccountController处理用户账号的注册、登录、注销、密码修改等操作。 是一个具有一些基本操作的 Web API 控制器,例如插图。 您会注意到这个控制器是用[Authorize]属性装饰的。 所以只有经过认证的请求才能访问这个控制器:
[Authorize]
public class ValuesController : ApiController
{
...
}
让我们运行应用并浏览 URLhttp://localhost:61486/api/Values/。 您将得到以下授权错误:

图 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 操作方法中发布一个用户实例来注册一个用户,如下所示:

图 12 -发布一个 User 实例到 Web API
现在,我们有一个注册用户,用户名steve@jobs.com和密码Password@1。 让我们将请求体中 URL 编码的数据发送给令牌端点,如下所示:
{
"grant_type": "password",
"username": "steve@jobs.com",
"Password": "Password@1"
}
让我们使用 Fiddler 将这个实例发送到Token端点,如下所示:

图 13 -将具有授权类型的 User 实例发布到令牌端点
我们将在 Set-Cookie 报头中收到一个带有令牌的响应,如下图所示:

图 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视图中,我们打印分配给视图袋的访问令牌,如下图所示:

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

图 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。
让我们把安全措施提高到更高一级!
五、在 Web API 中使用认证过滤器启用基本认证
本章将展示如何使用实现 HTTP 基本认证方案的认证过滤器为单个控制器或动作设置认证方案,并讨论使用基本认证的优缺点。
在本章中,我们将学习以下主题:
- 使用 IIS 进行基本认证
- 具有自定义成员资格的基本认证
- 使用认证过滤器的基本认证
- 设置认证过滤器
- 实现 Web API 认证过滤器
- 设置错误结果
- 将认证过滤器与主机级认证相结合
使用 IIS 的基本认证
Internet****Information Services(IIS)允许基于其 Windows 凭据对用户进行认证。 因此,用户必须拥有域服务器帐户。 IIS 中的基本认证构建为使用 Windows 凭据进行认证。
下面的步骤将使用 IIS 启用基本认证:
-
打开您的ASP。 从 Visual Studio 的Start页面中获取 asp.net 应用。
-
打开Web。 config文件。
-
在Web 界面中设置认证方式为Windows。 config文件:
-
打开IIS 管理器。
-
进入特性视图。
-
Select Authentication in IIS Manger:
![Basic authentication with IIS]()
图 1 -在特征视图中选择认证
-
Disable Anonymous Authentication and enable Basic Authentication:
![Basic authentication with IIS]()
图 2 -右击并启用基本认证
注意事项
如果你不觉得基本认证****验证下,去程序和功能,打开或关闭【显示】窗口组件,并使基本认证下的【病人】IIS|万维网服务|【t16.1】安全。
**# 具有自定义会员资格的基本认证
我们刚刚看到了如何使用 IIS 启用基本认证。 然而,如果该网站是一个公开可用的网站,则不可能使用 Windows 凭据对用户进行认证。 在这种情况下,网站应该使用 ASP 进行认证。 网会员提供者。 为了实现这一点,我们需要实现具有基本认证的自定义成员。
具有基本认证的自定义成员可以使用 HTTP 模块实现,如下所示:
public class BasicAuthHttpModule : IHttpModule
{
//...
}
我们需要创建两个方法,并将它们与Init方法中的AuthenticateRequest和EndRequest事件挂钩,如下代码所示:
public void Init(HttpApplication context)
{
context.AuthenticateRequest +=
OnApplicationAuthenticateRequest;
context.EndRequest += OnApplicationEndRequest;
}
如您所见,OnApplicationAuthenticateRequest被添加或注册到HttpApplication的AuthenticateRequest事件中,OnApplicationEndRequest被添加或注册到HttpApplication的EndRequest事件中。
OnApplicationAuthenticateRequest方法应该检查请求头中的Authorization请求,如下所示:
var request = HttpContext.Current.Request;
var authHeader = request.Headers["Authorization"];
if (authHeader != null)
{
//...
}
如果Authorization头包含基本认证的信息,那么应该从凭证中提取用户名和密码,并使用 ASP 进行验证。 注入到 HTTP 模块中的 NET 成员资格提供者,如下所示:
var encoding = Encoding.GetEncoding("iso-8859-1");
var credentials = encoding.GetString(Convert.FromBase64String(authHeader.Parameter));
int separator = credentials.IndexOf(':');
string name = credentials.Substring(0, separator);
string password = credentials.Substring(separator + 1);
如果获得的用户名和密码有效,则创建 IPrincipal 并将其分配给 HTTP 上下文的当前用户,如下所示:
// If credential is valid, then build the principal
var identity = new GenericIdentity(name);
Thread.CurrentPrincipal = new GenericPrincipal(identity, null));
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
在web.config文件的system.webServer部分下添加以下代码来启用 HTTP 模块:
<modules>
<add name="BasicAuthHttpModule"
type="ContactLookup.Modules.BasicAuthHttpModule, AssemblyName"/>
</modules>
用程序集的实际名称替换AssemblyName,不包括dll扩展名。 此外,我们还需要禁用其他认证,例如表单和 Windows 认证。
使用认证过滤器的基本认证
随着 ASP 的发布。 在基本认证中使用认证过滤器而不是使用 HTTP 模块是最佳实践。 按照给出的步骤使用认证过滤器实现基本认证:
-
在 Visual Studio 的Start页面中创建新项目。
-
选择 Visual c# Installed Template 命名为Web。
-
选择ASP.NET Web 应用。
-
Name the project
Chapter05.BasicAuthenticationand click OK:![Basic authentication using an authentication filter]()
图 3 -我们已经命名了 ASP.NET Web 应用作为“第五章”。 BasicAuthentication”
-
Select the MVC template in the New ASP.NET Project dialog.
![Basic authentication using an authentication filter]()
图 4 -选择 MVC 模板,在添加文件夹和核心引用中检查 Web API
-
检查Web API,然后点击好在添加文件夹和核心参考文献,【显示】认证个人用户帐户:
-
添加名为
BasicAuthorizeAttribute的过滤器,继承AuthorizeAttribute并将代码替换为下面给出的代码:namespace Chapter05.BasicAuthentication.Filters { public class BasicAuthorizeAttribute : System.Web.Http.AuthorizeAttribute { private const string BasicAuthResponseHeader = "WWW-Authenticate"; private const string BasicAuthResponseHeaderValue = "Basic"; public override void OnAuthorization(HttpActionContext actionContext) { try { var authValue = actionContext.Request.Headers.Authorization; if (authValue != null && !String.IsNullOrWhiteSpace(authValue.Parameter) && authValue.Scheme == BasicAuthResponseHeaderValue) { var credentials = ParseAuthorizationHeader(authValue.Parameter); if (credentials != null) { // Check if the username and passowrd in credentials are valid against the ASP.NET membership. // If valid, the set the current principal in the request context var identity = new GenericIdentity(credentials.Username); actionContext.RequestContext.Principal = new GenericPrincipal(identity, null); } } else { actionContext.Response = GetUnauthorizedResponse(); return; } } catch (Exception) { actionContext.Response = GetUnauthorizedResponse(); return; } } private Credentials ParseAuthorizationHeader(string authHeader) { var credentials = Encoding.ASCII.GetString(Convert.FromBase64String(authHeader)).Split(new[] { ':' }); if (credentials.Length != 2 || string.IsNullOrEmpty(credentials[0]) || string.IsNullOrEmpty(credentials[1])) return null; return new Credentials() { Username = credentials[0], Password = credentials[1], }; } private HttpResponseMessage GetUnauthorizedResponse() { var response = new HttpResponseMessage(HttpStatusCode.Unauthorized); response.Headers.Add(BasicAuthResponseHeader, BasicAuthResponseHeaderValue); return response; } } public class Credentials { public string Username { get; set; } public string Password { get; set; } } } -
正如您在代码中看到的,方法检查请求头中的
Authorization。 如果Authroization头和基本认证信息都可用,那么它将尝试从Base64编码的令牌值中提取用户名和密码。 提取的用户凭证将根据 ASP 进行验证.NET 成员认证。 -
Add a Web API controller named
ContactsControllerand replace the code with the following code:namespace Chapter05.BasicAuthentication.Api { public class ContactsController : ApiController { IEnumerable<Contact> contacts = new List<Contact> { new Contact { Id = 1, Name = "Steve", Email = "steve@gmail.com", Mobile = "+1(234)35434" }, new Contact { Id = 2, Name = "Matt", Email = "matt@gmail.com", Mobile = "+1(234)5654" }, new Contact { Id = 3, Name = "Mark", Email = "mark@gmail.com", Mobile = "+1(234)56789" } }; [BasicAuthorize] // GET: api/Contacts public IEnumerable<Contact> Get() { return contacts; } } }也可以在控制器或应用级别配置
BasicAuthorize属性。 这可以通过用这个属性装饰控制器或者在一个global.asax文件中配置它来实现,以便在应用中启用它。 -
ContactsController中的Get操作使用BasicAuthorize属性进行装饰,该属性是我们为基本认证创建的自定义属性。 因此,只有在 header 中具有有效的基本认证细节的请求才能访问 API 控制器中的GET动作。
设置认证过滤器
http://aspnet.codeplex.com提供了名为IdentityBasicAuthenticationAttribute的认证过滤器的示例代码,该过滤器实现 HTTP 基本访问认证方案(RFC 2617)。 我们可以使用这个[IdentityBasicAuthentication]认证过滤器,并将其应用于动作级别、控制器级别或可应用于所有控制器和动作的全局级别。
动作级认证过滤器
要在操作级别应用基本认证过滤器,我们需要用[IdentityBasicAuthentication]过滤器装饰各自的操作,如下所示:
// Require authenticated requests.
public class ContactsController : ApiController
{
public HttpResponseMessage GetContact() { . . . }
// Enable Basic authentication for this action
[IdentityBasicAuthentication]
public HttpResponseMessage PostContact(Contact contact) { . . . }
}
控制器级认证过滤器
如果我们在控制器级别修饰基本认证过滤器,那么访问该控制器内的所有操作需要一个经过认证的请求。 在控制器级应用基本认证过滤器如下所示:
// Enable Basic authentication for this controller [IdentityBasicAuthentication]
// Require authenticated requests.
[Authorize]
public class ContactsController : ApiController
{
public HttpResponseMessage GetContact() { . . . }
public HttpResponseMessage PostContact(Contact contact) { . . . }
}
全局级别的认证过滤器
通过将该过滤器添加到webApiConfig类文件中的过滤器集合中,我们可以在 Web API 中全局应用任何过滤器,如下所示。 这段代码将IdentityBasicAuthentication过滤器添加到过滤器集合中,如下所示:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new IdentityBasicAuthenticationAttribute());
//...
}
}
实现 Web API 认证过滤器
Web API 中的认证过滤器必须实现System.Web.Http.Filters.IAuthenticationFilter接口。 该接口包含一个布尔类型的AllowMultiple属性,它指示可以为单个程序元素指定该属性的多个实例。 它有两个方法,即验证请求中的凭证的AuthenticateAsync和在需要时向响应附加认证挑战的ChallengeAsync。
由于过滤器可以修饰为控制器和操作,我们还需要继承System.Attribute。
在 Web API 控制器中执行操作之前,它首先构建一个在控制器级别和特定操作级别全局配置的认证过滤器列表。 然后它在列表中找到的每个过滤器中调用AuthenticateAsync方法。 每个过滤器中的AuthenticateAsync方法验证请求中的凭据,如果凭据验证成功,则创建 IPrincipal 并将其附加到请求中。 CodePlex 中 Basic Authentication sample 中的AuthenticateAsync方法的代码片段如下:
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
// 1\. Look for credentials in the request.
HttpRequestMessage request = context.Request;
AuthenticationHeaderValue authorization = request.Headers.Authorization;
// 2\. If there are no credentials, do nothing.
if (authorization == null)
{
return;
}
// 3\. If there are credentials but the filter does not recognize the
// authentication scheme, do nothing.
if (authorization.Scheme != "Basic")
{
return;
}
// 4\. If there are credentials that the filter understands, try to validate them.
// 5\. If the credentials are bad, set the error result.
if (String.IsNullOrEmpty(authorization.Parameter))
{
context.ErrorResult = new AuthenticationFailureResult("Missing credentials", request);
return;
}
Tuple<string, string> userNameAndPasword = ExtractUserNameAndPassword(authorization.Parameter);
if (userNameAndPasword == null)
{
context.ErrorResult = new AuthenticationFailureResult("Invalid credentials", request);
}
string userName = userNameAndPasword.Item1;
string password = userNameAndPasword.Item2;
IPrincipal principal = await AuthenticateAsync(userName, password, cancellationToken);
if (principal == null)
{
context.ErrorResult = new AuthenticationFailureResult("Invalid username or password", request);
}
// 6\. If the credentials are valid, set principal.
else
{
context.Principal = principal;
}
}
成功创建 IPrincipal 之后,Web API 在列表中的可用过滤器中执行ChallengeAsync方法,向响应添加挑战。 方法的代码片段如下所示:
public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
var challenge = new AuthenticationHeaderValue("Basic");
context.Result = new AddChallengeOnUnauthorizedResult(challenge, context.Result);
return Task.FromResult(0);
}
设置错误结果
如果列表中的任何筛选器发现凭据无效,那么它将在AuthenticateAsync方法的上下文参数中设置ErrorResult。 在过滤器的AuthenticateAsync方法中,AuthenticationFailureResult实例以适当的错误消息被分配给上下文:
// If the sufficient information for credentials not supplied.
if (String.IsNullOrEmpty(authorization.Parameter))
{
context.ErrorResult = new AuthenticationFailureResult("Missing credentials", request);
return;
}
Tuple<string, string> userNameAndPasword = ExtractUserNameAndPassword(authorization.Parameter);
string userName = userNameAndPasword.Item1;
string password = userNameAndPasword.Item2;
IPrincipal principal = await AuthenticateAsync(userName, password, cancellationToken);
// if the provided username and password is not valid
if (principal == null)
{
context.ErrorResult = new AuthenticationFailureResult("Invalid username or password", request);
}
将认证过滤器与主机级认证相结合
主机级的认证将由主机(如 IIS)本身在请求到达 Web API 框架之前执行。 我们可以通过调用 Web API 配置中的config.SuppressHostPrincipal()方法来禁用 Web API 中的主机级认证,如下所示:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.SuppressHostPrincipal();
}
}
最佳实践是在 Web API 中禁用主机级认证,并在应用的其余部分启用它。 一旦禁用了主机级认证,我们就可以应用前面在应用级、控制器级或 Web API 操作级创建的认证过滤器。
总结
万岁! 我们只是使用基本认证来保护我们的 Web API。
您了解了如何在 IIS 中配置基本认证,并使用自定义成员资格实现了基本认证。
我们已经看到了如何在不同的级别上设置认证过滤器,并一步一步地介绍了如何在 ASP 中实现认证过滤器。 净 Web API。
您还学习了如何在认证失败时设置错误结果。
最后,我们看到了如何在 ASP 中抑制主机级认证。 净 Web API。
在下一章中,让我们使用表单和 Windows 认证来保护 Web API。**
六、使用窗体和 Windows 认证保护 Web API
本章将介绍如何使用窗体和 Windows 认证来保护 Web API。 您还将了解在 Web API 中使用 Forms 和 Windows 认证的优点和缺点。
在本章中,我们将涵盖以下主题:
- 表单验证的工作
- 在 Web API 中实现表单认证
- 讨论 Windows 集成认证
- 讨论了使用 Windows 集成认证机制的优缺点
- 配置 Windows 认证
- 讨论基本认证和 Windows 认证之间的区别
- 在武士刀中启用 Windows 认证
表单验证工作
用户凭证将在 forms 认证中使用 HTML 表单提交给服务器。 这可以在 ASP 中使用.NET Web API,仅当它被 Web 应用使用时。 表单验证是在 asp.net 下构建的.NET,并使用 asp.net.NET 成员资格提供程序来管理用户帐户。 表单认证要求浏览器客户机将用户凭据传递给服务器。 它在请求中发送用户凭证,并使用 HTTP cookie 进行认证。
让我们列出表单验证的逐步过程,如下:
- 浏览器试图访问需要经过认证的请求的受限操作。
- 如果浏览器发送一个未经认证的请求,那么服务器将响应 HTTP 状态
302 Found,并触发 URL 重定向到登录页面。 - 要发送经过认证的请求,用户输入用户名和密码,然后提交表单。
- 如果凭证是有效的,服务器响应 HTTP 302 状态码,该状态码发起浏览器将页面重定向到原始请求的 URL,并在响应中使用认证 cookie。
- 现在,来自浏览器的任何请求都包含认证 cookie,服务器将授予对任何受限资源的访问权。
在 Web API 中实现表单认证
要将凭据发送到服务器,我们需要提交一个 HTML 表单。 让我们在 ASP 中使用 HTML 表单或 HTML 视图。 净 MVC 应用。
在 ASP 中实现表单验证的步骤.NET MVC 应用如下:
-
在 Visual Studio 的Start页面中创建新项目。
-
选择 Visual c# Installed Template 命名为Web。
-
选择ASP.NET Web 应用在中心面板。
-
Name the project as
Chapter06.FormsAuthenticationand click OK:![Implementing Forms authentication in Web API]()
图 1 -我们已经命名了 ASP.NET Web 应用”第六章。 FormsAuthentication”
-
在新 ASP. net 中中选择MVC模板.NET 项目对话框。
-
Tick Web API under Add folders and core references and press OK, leaving the Authentication to Individual User Accounts:
![Implementing Forms authentication in Web API]()
图 2 -选择 MVC 模板,在添加文件夹和核心引用中检查 Web API
-
Forms authentication configuration can be included in the
web.configfile, as follows:<system.web> <authentication mode="Forms"> <forms loginUrl="~/Account/LogOn" timeout="30" /> </authentication> </system.web>在此配置中,我们将认证模式设置为
"Forms",并配置了loginurl,以便应用在请求未经过认证时重定向到已配置的页面进行登录。 -
在Models文件夹中,添加一个名为
Contact.cs的类,代码如下:namespace Chapter06.FormsAuthentication.Models { public class Contact { public int Id { get; set; } public string Name { get; set; } public string Email { get; set; } public string Mobile { get; set; } } } -
添加一个名为
ContactsController的 Web API 控制器,代码片段如下:
正如您在前面的代码中看到的,我们已经用[Authorize]属性装饰了ContactsController中的Get()动作。 因此,这个 Web API 操作只能由经过认证的请求访问。 这个操作的未经验证的请求将使浏览器重定向到登录页面,并允许用户注册或登录:

图 3 -重定向登录页面认证
一旦登录,任何试图访问此操作的请求都将在经过认证后被允许。 这个是因为浏览器会自动发送会话 cookie,表单验证使用这个 cookie 来验证请求:

图 4 -开发工具中显示的认证 Cookie
注意事项
由于表单认证发送未加密的凭证,所以使用 SSL 保护网站是非常重要的。 参考第二章、为 ASP 启用 SSL.NET Web API,学习如何为您的应用设置 SSL。
*# 什么是 Windows 集成认证?
集成 Windows 认证是一种基于 SPNEGO、Kerberos 和 NTLMSSP 协议的认证机制。 它处理 IIS、IE 和活动目录之间的连接的自动认证。
使用 Windows 集成认证机制的优缺点
首先让我们看看 Windows 认证的优点。 Windows 认证是在 IIS 中构建的。 它不随请求一起发送用户凭证。 这种认证机制最适合于 intranet 应用。
然而,有了这些优点,Windows 认证机制也没有什么缺点。 它需要 Kerberos, Kerberos 基于票据或 NTLM,后者是一种客户机应该支持的微软安全协议。 客户端 PC 必须在 active directory 域下。
配置 Windows 认证
让我们实现对 ASP 的 Windows 认证.NET MVC 应用以下的步骤:
-
在 Visual Studio 的Start页面中创建新项目。
-
选择Visual c# Installed Templatenamed Web。
-
选择ASP.NET Web 应用在中心面板。
-
Name the project
Chapter06.WindowsAuthenticationand click OK:![Configuring Windows Authentication]()
图 5 -我们已经命名了 ASP.NET Web 应用”第六章。 WindowsAuthentication”
-
Change the Authentication mode to Windows Authentication:
![Configuring Windows Authentication]()
图 6 -在更改认证窗口中选择 Windows 认证
-
在新 ASP. net 中选择MVC模板.NET 项目对话框。
-
Tick Web API under Add folders and core references and click OK:
![Configuring Windows Authentication]()
图 7 -选择 MVC 模板,在添加文件夹和核心引用中检查 Web API
-
在 Models 文件夹下,添加一个名为
Contact.cs的类,代码如下:namespace Chapter06.FormsAuthentication.Models { public class Contact { public int Id { get; set; } public string Name { get; set; } public string Email { get; set; } public string Mobile { get; set; } } } -
添加一个名为
ContactsController的 Web API 控制器,代码如下:namespace Chapter06.FormsAuthentication.Api { public class ContactsController : ApiController { IEnumerable<Contact> contacts = new List<Contact> { new Contact { Id = 1, Name = "Steve", Email = "steve@gmail.com", Mobile = "+1(234)35434" }, new Contact { Id = 2, Name = "Matt", Email = "matt@gmail.com", Mobile = "+1(234)5654" }, new Contact { Id = 3, Name = "Mark", Email = "mark@gmail.com", Mobile = "+1(234)56789" } }; [Authorize] // GET: api/Contacts public IEnumerable<Contact> Get() { return contacts; } } } -
The
Get()action inContactsControlleris decorated with the[Authorize]attribute. But in Windows authentication, any request is considered as authenticated request if the client relies on the same domain. So no explicit login process is required to send an authenticated request to call theGet()action.
### 注意事项
注意:Windows 认证配置在`Web.config`文件中,如下所示:
```
<system.web>
<authentication mode="Windows" />
</system.web>
```
基本认证与 Windows 认证的区别
Windows 认证通过在 Windows 域中根据用户帐户验证凭据来验证用户。
基本认证验证表单中针对存储在数据库中的用户帐户提供的凭据。
在武士刀中启用 Windows 认证
Katana 是一个项目集合,通过System.Web和System.Net.HttpListener的各种微软组件来支持 OWIN。
我们可以在基于 owen 的应用中插入 Web API,而不是依赖于System.Web文件。 这可以通过安装Microsoft.AspNet.WebApi.OwinNuGet 包来实现,它允许将 Web API 添加到中间件管道中。
按照以下步骤创建一个控制台应用,并在武士刀中启用 Windows 认证:
-
在 Visual Studio 的Start页面中创建新项目。
-
选择Visual c# Installed Template命名Windows Desktop。
-
在中间窗格中选择控制台应用。
-
Name the project as
Chapter06.WindowsAuthenticationKatanaand click OK:![Enabling Windows authentication in Katana]()
图 8 -我们将控制台应用命名为“第六章” WindowsAuthenticationKatana”
-
Install NuGet Package named Microsoft.Owin.SelfHost from NuGet Package Manager:
![Enabling Windows authentication in Katana]()
图 9 -安装名为 microsoft . owen . selfhost 的 NuGet 包
-
用以下代码片段添加一个 Startup 类:
namespace Chapter06.WindowsAuthenticationKatana { class Startup { public void Configuration(IAppBuilder app) { var listener = (HttpListener)app.Properties["System.Net.HttpListener"]; listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication; app.Run(context => { context.Response.ContentType = "text/plain"; return context.Response.WriteAsync("Hello Packt Readers!"); }); } } } -
在
Program.cs的主功能中添加如下代码:using (WebApp.Start<Startup>("http://localhost:8001")) { Console.WriteLine("Press any Key to quit Web App."); Console.ReadKey(); } -
Now run the application and open
http://localhost:8001/in the browser, as shown in the following screenshot:![Enabling Windows authentication in Katana]()
图 10 -在浏览器中打开 Web App
-
如果使用 Fiddler 捕获请求,您将注意到请求头中有一个 Authorization Negotiate 条目。
-
尝试在 Fiddler 中调用
http://localhost:8001/,您将得到一个带有WWW-Authenticate报头的401 Unauthorized响应,这表明服务器附加了使用 Kerberos 或 NTLM 的Negotiate协议,如下所示:
总结
瞧! 我们只是使用表单和 Windows 认证来保护我们的 Web API。
在本章中,您了解了表单认证的工作方式以及它是如何在 Web API 中实现的。
您还学习了配置 Windows 认证,了解了使用 Windows 认证的优缺点。
然后您学习了在 Katana 中实现 Windows 认证机制。
在下一章中,让我们看看如何使用外部认证服务(如 Facebook 和 Twitter)来保护 Web API。*
七、使用 ASP.NET Web API 使用外部认证服务
本章将帮助您理解启用 OAuth/OpenID 和社交媒体认证时需要使用外部认证服务。
在本章中,我们将涵盖以下主题:
- 使用 OWIN 外部认证服务
- 实现 Facebook 认证
- 实现微博认证
- 实现谷歌认证
- 实现微软认证
- 讨论验证
使用 OWIN 外部认证服务
使用外部认证服务进行认证可以减少实现内部认证机制所需的开发时间。 大多数网络用户在社交媒体网站上都有账户,比如 Facebook 和 Twitter,以及其他服务,比如微软和谷歌。 使用外部认证服务可以节省用户为您的 web 应用创建另一个帐户的时间。
ASP.NET 为外部认证服务(如 Facebook、Twitter、Microsoft 和谷歌)提供内置支持。
创建 ASP。 净 MVC 应用
让我们创建一个 ASP.NET MVC 应用来演示如何实现外部认证解决方案,如下:
-
在 Visual Studio 中从“开始”页创建新项目。
-
选择 Visual c# Installed Template,命名为 Web。
-
选择 ASP.NET Web 应用。
-
Name the project as
Chapter07.ExternalAuthenticationand click OK:![Creating an ASP.NET MVC Application]()
图 1 -我们已经命名了 ASP.NET Web 应用作为“第 07 章。 ExternalAuthentication”
-
在新 ASP. net 中选择MVC模板.NET 项目对话框。
-
Check Web API and click OK under Add folders and core references for, leaving the Authentication to Individual User Accounts:
![Creating an ASP.NET MVC Application]()
图 2 -选择 MVC 模板,在添加文件夹和核心引用中检查 Web API
-
将名为
Contact.cs的模型类添加到 Models 文件夹中,代码如下:namespace Chapter07.ExternalAuthentication.Models { public class Contact { public int Id { get; set; } public string Name { get; set; } public string Email { get; set; } public string Mobile { get; set; } } } -
添加一个名为
ContactsController的 Web API 控制器,并用以下代码替换代码:namespace Chapter07.ExternalAuthentication.Api { public class ContactsController : ApiController { IEnumerable<Contact> contacts = new List<Contact> { new Contact { Id = 1, Name = "Steve", Email = "steve@gmail.com", Mobile = "+1(234)35434" }, new Contact { Id = 2, Name = "Matt", Email = "matt@gmail.com", Mobile = "+1(234)5654" }, new Contact { Id = 3, Name = "Mark", Email = "mark@gmail.com", Mobile = "+1(234)56789" } }; [Authorize] // GET: api/Contacts public IEnumerable<Contact> Get() { return contacts; } } }
我们刚刚创建了一个 ASP.NET MVC 应用,并添加了一个名为ContactsController的 Web API 控制器来提供一个联系人详细信息列表。 ContactsController中的Get操作被[Authorize]属性修饰,只能使用经过认证的请求调用。 现在,让我们逐个添加外部认证解决方案来验证请求。
实现 Facebook 认证
要将 Facebook 认证服务应用到我们的应用中,我们首先需要在 Facebook 中创建一个开发者帐户,然后使用该开发者帐户在 Facebook 上创建一个应用。 这个应用将为我们提供一个应用 ID 和一个密钥,以便在我们的应用中启用 Facebook 认证。 按照给定的步骤从 Facebook 获得密钥:
-
使用您的凭证登录到 Facebook。
-
导航到 Facebook 开发者网站(https://developers.facebook.com/)。
-
Click the Register as a Developer menu item under the My Apps menu:
![Implementing Facebook authentication]()
图 3 -从菜单中选择注册为开发人员
-
Select Yes to accept the Facebook Platform Policy and Facebook Privacy Policy and click Register, which would successfully register you as a Facebook Developer. You can now add Facebook to your app or website:
![Implementing Facebook authentication]()
图 4 -点击 Register 注册成为 Facebook 开发者
-
Click Website in the Add a New App page:
![Implementing Facebook authentication]()
图 5 -在 Add a New App 页面选择 Website
-
Click Skip and Create App ID on the Quick Start for Website page:
![Implementing Facebook authentication]()
图 6 -导航到 Skip and Create App ID 链接
-
Provide Display Name and Namespace. Select Category under the model popup dialog. Then click the Create App ID button:
![Implementing Facebook authentication]()
图 7 -导航到 Skip 和 Create App ID link
-
Confirm the captcha as a security check by entering the text exactly in the same case, since it is case sensitive, and click Submit:
![Implementing Facebook authentication]()
图 8 -为了安全起见,确认验证码
-
Now the Dashboard for the created Facebook app will be shown with the details of the App ID, API Version, and App Secret key:
![Implementing Facebook authentication]()
图 9 - App ID 和 Secret key 在仪表盘下可用
-
Navigate to Settings and click on Add Platform:

图 10 - App ID 和 Secret key 在仪表盘下可用
- Click Website under the Select Platform model popup dialog:

图 11 -为平台选择网站
- Enter your website URL in Site URL under the Website panel that was added. You can also use localhost URL as Site URL during development:

图 12 -提供应用 URL
- 单击Save Changes按钮保存所做的更改。
- 复制App ID和App Secret Key值,以便配置应用代码。 当用户试图使用 Facebook 帐户登录时,这些值将从您的网站传递给 Facebook 提供商。
- 退出 Facebook 开发者网站。
- 打开 ASP.NET MVC 应用,我们在上一节中创建。
- 打开
App_Start文件夹下的Startup.Auth.cs类文件。 - 将复制的App ID和App Secret Key在以下代码中按照
ConfigureAuth方法更新:
```
app.UseFacebookAuthentication(
appId: "",
appSecret: "");
```
- Now, run the application and try to log in using Facebook by clicking the Facebook button. Your website will be redirected to Facebook in order to authenticate the login request:

图 13 -单击 Facebook 来验证用户以访问您的应用
- Facebook will confirm with the user whether they are fine with the app that may access the public profile information. Your public profile includes name, profile picture, age range, gender, language, country, and other public information:

图 14 -确认访问您的个人信息
- Clicking Okay will redirect you back to the application and you will be asked to provide your email ID in order to register the user who has logged in via Facebook, by associating his Facebook account. The redirect happens as we supplied a redirect URL for our application:

图 15 -通过注册你的电子邮件 id 来关联你的 Facebook 账户
实现 Twitter 认证
在实现 Facebook 认证时,我们还需要创建一个 Twitter 开发人员帐户,并需要从应用向 Twitter 提供消费者密钥和消费者秘密值,以便进行认证。
按照给定的步骤从您的应用的 Twitter 开发人员帐户中获取消费者密钥和秘密值:
-
登录您的 Twitter 账号(https://twitter.com/)。
-
导航到 Twitter 开发人员站点(https://dev.twitter.com/)。
-
向下滚动到页脚,点击Tools下的Manage Your Apps。
-
Click the Create New App button in the redirected page:
![Implementing Twitter authentication]()
图 16 -创建你的 Twitter 应用
-
Provide your Application Details such as Name, Description, Website, and Callback URL. Tick agree to the developer agreement in the Create an application page. The Callback URL is the URL of our application that Twitter needs to redirect to after the successful authentication:
![Implementing Twitter authentication]()
图 17 -提供创建应用的细节
-
点击创建您的 Twitter应用按钮。
-
可以找到消费者关键和秘密值,点击管理密钥和访问令牌下的应用设置【显示】细节部分的选项卡创建应用。 复制消费者密钥和秘密值。
-
在App_Start文件夹下,打开
Startup.Auth.cs类文件。 -
更新复制的消费者密钥和消费者秘密值,如下代码在
ConfigureAuth方法下:app.UseTwitterAuthentication( consumerKey: "", consumerSecret: ""); -
Run your application and click on the Twitter button in the following page to log in using Twitter:

图 18 -单击 Twitter 来验证用户
- Your application will redirect to Twitter for authentication. Click Sign In to authorize the Twitter app to use your account and authenticate the access to your application:

图 19 -通过单击 sign in 来授权您的应用
- You need to provide your email ID to register and associate your Twitter account:

图 20 -将 Twitter 应用与应用配置文件关联
- 退出 Twitter 网站。
实现谷歌认证
到目前为止,我们已经看到了如何通过 Facebook 和 Twitter 等社交媒体网站整合外部认证。 现在让我们看看如何集成谷歌认证。 我们需要在谷歌开发人员控制台中创建一个项目和一个创建客户端 ID 和秘密密钥,并且应该在我们的应用中配置这个客户端 ID 和秘密密钥。
按照给定的步骤从应用的谷歌开发控制台获取客户端 ID 和密钥值:
-
登录您的谷歌账号(https://www.google.com/)。
-
打开谷歌开发控制台站点(https://console.developers.google.com/)。
-
Click Create Project:
![Implementing Google authentication]()
图 21 -在谷歌开发人员控制台中创建项目
-
Enter the Project Name and click the Create button:
![Implementing Google authentication]()
图 22 -提供项目名称和创建
-
点击API&auth|API,使能谷歌+ API。
-
Click APIs & auth | Credentials and select the OAuth consent screen tab. Provide the Project Name and Save:
![Implementing Google authentication]()
图 23 -提供你的电子邮件地址
-
Select OAuth 2.0 client ID under Add credentials:
![Implementing Google authentication]()
图 24 -在“添加凭证”按钮下拉框中选择 API 密钥 这张截图是 2015 年 9 月拍摄的
-
Create client ID by selecting web application as Application Type and providing authorized JavaScript origins and redirect URIs:
![Implementing Google authentication]()
图 25 -配置授权的应用类型、JavaScript 起源和重定向 uri
-
The client ID and secret key will be shown on a modal popup, as follows:
![Implementing Google authentication]()
图 26 -复制 Client ID 和 Secret key 值
-
复制Client****IDandSecret key。
-
在App_Start文件夹下,打开
Startup.Auth.cs类文件。 -
更新复制的客户端 ID和Secret Key值,如下代码在
ConfigureAuth方法下: -
运行 web 应用,从外部认证服务登录页面使用谷歌登录。
-
The web application will be redirected to Google for authentication. Click Allow to authorize the application to access the user profile and authenticate using your Google account:

图 27 -提供创建应用的细节
- You need to provide your email ID to register and associate your Google account:

图 28 -通过注册你的电子邮件 id 来关联你的谷歌账户
- 退出谷歌站点。
实现 Microsoft 认证
Microsoft 认证就像利用终端用户的企业账户来验证他们的身份。 我们需要创建一个应用并配置重定向 URL。 然后从 Microsoft 开发人员帐户的应用设置中复制客户端 ID 和密钥,并在我们的应用代码中配置它们。
按照给定的步骤从 Microsoft 开发人员门户获取客户端 ID 和密钥值:
登录到您的微软帐户(https://www.live.com/)。
-
导航到 Microsoft 开发人员中心站点(https://account.live.com/developers/applications/)。
-
Provide the Application name and click the I accept button:
![Implementing Microsoft authentication]()
图 29 -让你的应用使用微软帐户
-
Click on API Settings and configure Redirect URLs:
![Implementing Microsoft authentication]()
图 30 -为应用配置重定向 URL
-
Click App Settings and copy the Client ID and Client Secret value:
![Implementing Microsoft authentication]()
图 31 -复制客户端 ID 和 Secret Key 值
-
在App_Start文件夹下,打开
Startup.Auth.cs类文件。 -
更新复制的客户端 ID和Secret Key值,如下代码在
ConfigureAuth方法下: -
运行 web 应用,从外部认证服务登录页面使用 Microsoft 帐号登录。
-
On clicking the Microsoft button for login, your application will redirect to Microsoft Live for authentication. Click Yes to authorize the application and authenticate the user that is using your Microsoft account:
![Implementing Microsoft authentication]()
图 32 -允许你的应用从微软账户访问用户信息
-
You need to provide your email ID to register and associate your Microsoft account:
![Implementing Microsoft authentication]()
图 33 -通过注册你的电子邮件来关联你的微软账户
-
退出 Microsoft 站点。
讨论认证
在前面的部分中,我们已经看到了外部认证是如何执行的。 这一切都始于注册从外部认证提供者检索的用户详细信息。 在用户详细信息成功注册到我们的应用后,我们不需要再次在应用中存储用户详细信息。
这一次,外部认证提供者不会为了访问用户的公共概要信息而询问用户的权限,因为用户已经将权限授予了我们的应用。 因此,外部认证提供者将只验证用户,而我们的应用将绕过现有用户的注册过程。
总结
瞧! 我们只是使用外部认证机制来保护我们的 Web API。
在本章中,您了解了 OWIN 外部认证以及如何使用它来保护我们的 Web API。
您还学习了如何在外部认证服务(如 Facebook、Twitter、谷歌和 Microsoft)上注册为开发人员,并在那里创建一个应用,分别获取客户端 ID 和密钥以及消费者 ID 和密钥。
然后,您了解了如何配置从源代码中的外部认证服务复制的 ID 和密钥,以便在应用中实现外部认证。
最后,我们运行了应用并看到了外部认证的工作情况。
在下一章中,让我们学习如何保护我们的 Web API 免受跨站点请求伪造攻击。
让我们准备进攻吧!
八、避免 Web API 中的跨站点请求伪造攻击
本章将帮助您在 ASP 中避免跨站点请求伪造(CSRF)。 净 Web API。 使用基于 API 密钥的认证或更复杂的机制(如 OAuth)来避免 CSRF 攻击。
在本章中,我们将涵盖以下主题:
- 什么是 CSRF 攻击?
- 使用 HTML 表单或 Razor 视图的防伪令牌
- 使用 AJAX 防伪令牌
什么是 CSRF 攻击?
在维基百科(https://en.wikipedia.org/wiki/Cross-site_request_forgery/),跨站点请求伪造(CSRF 或 XSRF),也称为一键攻击或会话骑,是一种恶意的利用网站,未经授权的命令是传播从一个网站的用户信任。 与跨站点脚本编制(XSS)不同,它利用用户对特定站点的信任,而 CSRF 利用站点在用户浏览器中的信任。
简单地说,这种类型的攻击是由恶意站点向登录到易受攻击站点的用户发送请求时发起的。

图 1 - CSRF 攻击如图所示
使用 HTML 表单或 Razor 视图的防伪令牌
防伪令牌或请求验证令牌在 ASP 中使用.NET MVC 避免 CSRF 攻击。 防伪令牌或请求验证令牌有助于防止 CSRF 攻击。 . net 框架内置了创建和验证防伪令牌的支持。 MVC Razor 引擎中的@Html.AntiForgeryToken()方法创建了防伪令牌。 防伪令牌的验证可以通过使用[ValidateAntiForgeryToken]属性修饰控制器或动作来实现。
防伪代币是如何工作的?
让我们看看服务器如何基于防伪令牌接受或拒绝请求。 以下是制作防伪代币所涉及的步骤:
- 客户端发送一个带有表单的 MVC 视图请求。
- 服务器返回请求的视图和两个标记,一个通过 cookie 发送,另一个通过设置为视图形式的隐藏字段发送。
- 当客户端在
view中提交表单时,这两个令牌将被传递回服务器。 - 服务器在连续的请求中查找这两个令牌,如果没有找到令牌,那么请求将被拒绝。
防伪令牌的保护和认证令牌的保护一样重要。 因此,使用 SSL 是最佳实践。 参考第二章、为 ASP 启用 SSL.NET Web API来查看如何为 ASP 启用SSL。 净 Web API。
使用 AJAX 防伪令牌
使用 AJAX 向服务器发送数据或从服务器获取数据是非常常见的。 AJAX 请求将 JSON 数据发送到服务器。 它不发送 HTML 表单数据。 为了通过 AJAX 发送令牌,我们需要使用自定义 HTTP 头。 使用 Razor 语法,我们可以通过调用AntiForgery.GetTokens()方法来生成令牌,并将其附加到请求中,如下代码所示:
<script>
@functions{
public string GetAntiForgeryTokenValue ()
{
string tokenInCookie, tokenInForm;
AntiForgery.GetTokens(null, out tokenInCookie, out tokenInForm);
return tokenInCookie + ":" + tokenInForm;
}
}
$.ajax("/api/contacts", {
type: "get",
headers: {
'AntiForgeryToken': '@GetAntiForgeryTokenValue()'
},
success: function (result) {
alert(JSON.stringify(result));
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(errorThrown + " Error");
}
});
</script>
当处理请求时,应该提取通过 AJAX 传递的令牌。 然后,必须使用以下代码片段中给出的AntiForgery.Validate方法验证提取的令牌。 如果令牌在请求中无效,AntiForgery.Validate方法抛出异常,如下所示:
void ValidateToken(HttpRequestMessage request)
{
string tokenInCookie = "";
string tokenInForm = "";
IEnumerable<string> tokenHeaders;
if (request.Headers.TryGetValues("AntiForgeryToken", out tokenHeaders))
{
var tokens = tokenHeaders.First().Split(':');
if (tokens.Length == 2)
{
tokenInCookie = tokens[0].Trim();
tokenInForm = tokens[1].Trim();
}
}
AntiForgery.Validate(tokenInCookie, tokenInForm);
}
我们可以为 Web API 实现一个授权过滤器,并将其修饰为动作,以便在调用动作之前验证防伪造令牌。 下面的代码片段展示了这样一个授权过滤器:
namespace Chapter08.AntiForgeryToken.Filters
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class ValidateAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
{
public Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
try
{
string tokenInCookie = "";
string tokenInForm = "";
IEnumerable<string> AntiForgeryTokenValue;
if (actionContext.Request.Headers.TryGetValues("AntiForgeryToken", out AntiForgeryTokenValue))
{
var antiForgeryTokens = AntiForgeryTokenValue.First().Split(':');
if (antiForgeryTokens.Length == 2)
{
tokenInCookie = antiForgeryTokens [0].Trim();
tokenInForm = antiForgeryTokens [1].Trim();
}
}
AntiForgery.Validate(tokenInCookie, tokenInForm);
}
catch (System.Web.Mvc.HttpAntiForgeryException e)
{
actionContext.Response = new HttpResponseMessage
{
StatusCode = HttpStatusCode.Forbidden,
RequestMessage = actionContext.ControllerContext.Request
};
var response = new TaskCompletionSource<HttpResponseMessage>();
response.SetResult(actionContext.Response);
return response.Task;
}
return continuation();
}
}
}
CSRF 攻击将被服务器以403 Forbidden错误拒绝。
总结
又短又甜,不是吗? 您刚刚学习了如何保护我们的 Web API 免受跨站点请求伪造攻击。
您还了解了 CSRF 攻击的含义以及它如何影响我们的 Web API。
然后学习了如何使用 HTML 表单和 AJAX 实现防伪令牌。
在下一章中,让我们看看如何在 Web API 中启用跨源资源共享。
让我们来看看起源吧!
九、在 ASP.NET Web API 中启用跨源资源共享(CORS)
本章将帮助您学习如何在 Web API 应用中启用 CORS。
在本章中,我们将涵盖以下主题:
- 歌珥是什么?
- 歌珥是如何工作的
- 设置允许的起源
- 设置允许的 HTTP 方法
- 设置允许的请求头
- 设置允许的响应头
- 在跨源请求中传递凭证
- 在不同范围启用 CORS
什么是 CORS?
根据同源策略,浏览器安全性避免任何从一个域到另一个域的 Web API 的 AJAX 请求,以防止恶意站点读取敏感数据或将其发布到另一个站点。 但是,在某些情况下,您可能需要启用其他域来调用您的 Web API。 这就是 CORS 出现的原因。
跨源资源共享(CORS)允许服务器根据配置忽略同源策略。 CORS 使服务器能够提供对其资源的受限访问。
CORS 如何工作
跨源资源共享设计提供了多种 HTTP 报头,如 Origin 和 Access-Control-Allow-Origin。 这些头将由支持 CORS 的浏览器为跨源请求设置。
让我们尝试访问以下未配置为支持 CORS 的 Web API 方法:
// GET: api/Contacts/id
public Contact Get(int id)
{
return contacts.FirstOrDefault(x => x.Id == id);
}
从不同的域访问此方法将导致以下错误:
XMLHttpRequest cannot load http://localhost:53858/api/contacts/1\. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:53870' is therefore not allowed access.
我们需要将一些特殊的头(例如请求中的 Origin 头)传递给配置了 CORS 的 Web API 方法。 下面的代码片段展示了 Web API 中的一个这样的方法:
[EnableCors(origins: "http://localhost:53870", headers: "*", methods: "*")]
// GET: api/Contacts
public IEnumerable<Contact> Get()
{
return contacts;
}
要访问此方法,客户机需要在 Origin 头中传递它的域,以便 Web API 服务器对其进行验证。 下面是带有 Origin 头的 HTTP 请求示例,它包含发送此请求的域的信息:
GET http://localhost:53858/api/contacts HTTP/1.1
Host: localhost:53858
Connection: keep-alive
Accept: */*
Origin: http://localhost:53870
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36
Referer: http://localhost:53870/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-GB,en-US;q=0.8,en;q=0.6
来自支持 CORS 并允许请求的服务器的 HTTP 响应示例如下:
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
Access-Control-Allow-Origin: http://localhost:53870
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Fri, 04 Sep 2015 05:08:14 GMT
Content-Length: 218
[{"Id":1,"Name":"Steve","Email":"steve@gmail.com","Mobile":"+1(234)35434"},{"Id":2,"Name":"Matt","Email":"matt@gmail.com","Mobile":"+1(234)5654"},{"Id":3,"Name":"Mark","Email":"mark@gmail.com","Mobile":"+1(234)56789"}]
正如您可以在前面的响应中看到的,Web API 服务器通过发送 Access-Control-Allow-Origin 报头来响应允许的域来确认客户端域。
设置允许的起源
我们需要在[EnableCors]属性的 origins 参数中提供以逗号分隔的域列表,如下代码片段所示:
[EnableCors(origins: " http://localhost:53870, http://localhost:53871", headers: "*", methods: "*")]
如您所见,这个 CORS 配置只允许来自两个域(http://localhost:53870/ 和http://localhost:53871/的 AJAX 请求,并拒绝来自其他域的任何请求。 我们也可以通过传递一个"*"通配符值来让 CORS 接受来自所有域的任何请求,如下所示:
[EnableCors(origins: "*", headers: "*", methods: "*")]
在将"*"通配符值应用到起源之前,最好重新考虑一下,因为它将允许任何域向您的 Web API 发出 AJAX 请求。
设置允许的 HTTP 方法
我们还可以限制 CORS 中的 HTTP 方法。 这可以通过为[EnableCors]属性的 methods 参数提供一个用逗号分隔的 HTTP 方法列表来实现,如下所示:
[EnableCors(origins: "http://localhost:53870", headers: "*", methods: "get,post")]
public class ContactsController : ApiController
{
IEnumerable<Contact> contacts = new List<Contact>
{
new Contact { Id = 1, Name = "Steve", Email = "steve@gmail.com", Mobile = "+1(234)35434" },
new Contact { Id = 2, Name = "Matt", Email = "matt@gmail.com", Mobile = "+1(234)5654" },
new Contact { Id = 3, Name = "Mark", Email = "mark@gmail.com", Mobile = "+1(234)56789" }
};
// GET: api/Contacts
public IEnumerable<Contact> Get()
{
return contacts;
}
// GET: api/Contacts/id
public Contact Get(int id)
{
return contacts.FirstOrDefault(x => x.Id == id);
}
}
前面的代码只允许GET和POST HTTP方法。 我们还可以通过向方法传递"*"通配符值来允许所有HTTP方法。
设置允许的请求头
有时,为了验证 CORS,浏览器在发送实际请求之前发送一个先决条件。 这些先决条件将使用HTTP OPTIONS方法,并且请求将具有以下访问控制请求头:
- Access-Control-Request-Method
- Access-Control-Request-Headers
应用于实际请求的 HTTP 操作方法名称提供给 Access-Control-Request-Method,应用于实际请求的以逗号分隔的报头列表提供给 Access-Control-Request-Headers。 下面的示例请求就是这样一个先决条件:
OPTIONS http://localhost:53858/api/contacts HTTP/1.1
Host: localhost:53858
Accept: */*
Origin: http://localhost:53870
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: accept, x-my-custom-header
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
Content-Length: 0
现在,服务器将响应关于它是否允许在实际请求的前提条件的特殊头中使用HTTP方法和头的信息。
在这里的先决条件中,浏览器询问服务器是否允许HTTP方法 PUT 以及 accept 和 x-my-custom-header 等请求头。 以下来自服务器的响应确认它可以允许所请求的方法和头:
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 0
Access-Control-Allow-Origin: http://localhost:53870
Access-Control-Allow-Headers: x-my-custom-header
Access-Control-Allow-Methods: PUT
Date: Sun, 30 Aug 2013 05:56:22 IST
如何配置服务器以允许某些请求头? 这将在[EnableCors]属性的headers参数中进行配置,如下所示:
[EnableCors(origins: "http://localhost:53870",
headers: "accept,content-type,origin,x-my-header", methods: "put")]
注意事项
注意,如果配置了任何特定的自定义头,那么最好至少包括“accept”、“content-type”和“origin”以及自定义头。
设置允许的响应头
- 有一些在响应中可用的默认头文件,并且由浏览器提供。 这些默认头文件包括 Content-Type、Content-Language、Cache-Control、Expires、Pragma 和 Last-Modified。 这些被称为简单响应头。
- 然而,在某些场景中,您可能希望在响应中公开一些特殊的头。 为了实现这一点,CORS 简化了
[EnableCors]属性中名为exposedHeaders的参数。 - 例如,让我们在响应中设置一个名为
"X-Custom-Header"的特殊头。 由于这是一个特殊的头文件,默认情况下浏览器不会在跨源请求中公开它。 为了使浏览器能够公开这个特殊的头,我们需要在[EnableCors]属性的exposedHeaders参数中设置头"X-Custom-Header",如下代码片段所示:
在跨源请求中传递凭证
浏览器在跨源请求中默认不传递 cookie 和 HTTP 认证方案等凭证。 为了在来自客户端的跨源请求中传递凭证,客户端必须将XMLHttpRequest.withCredentials设置为 true,如下所示:
$.ajax({
type: 'get',
url: ' http://localhost:53858 /api/contacts,
xhrFields: {
withCredentials: true
}
为了允许跨源请求中的凭据,应该将[EnableCors]属性上的SupportsCredentials属性设置为true,如下代码所示:
[EnableCors(origins: "http://chapter09client.com", headers: "*", methods: "*", SupportsCredentials = true)]
HTTP 响应还将具有 Access-Control-Allow-Credentials 报头,以指示浏览器,服务器可以在跨源请求中接受凭证。 使用 Cookie 或 Authorization 头,Web API 验证请求。 认证完成后,浏览器将继续在所有后续请求上向服务器传递认证信息。
注意事项
注意,我们不能将"*"通配符值设置为origins参数并启用它,以便同时支持凭据。
如果响应没有 Access-Control-Allow-Credentials 头,AJAX 调用方法将不会接收响应,因为浏览器没有公开它,这将导致 AJAX 请求失败。
在不同范围内启用 CORS
CORS 可以在不同的级别上启用。 我们可以在动作级别、控制器级别或全局级别设置 CORS。 让我们看看如何在不同的范围设置 CORS。
在动作级别启用
为了使 CORS 对特定的操作生效,我们需要用[EnableCors]属性装饰 action 方法,如下代码片段所示:
public class ContactsController : ApiController
{
[EnableCors(origins: "http://localhost:53870", headers: "*", methods: "*")]
public HttpResponseMessage GetContacts() { ... }
public HttpResponseMessage GetContact(int id) { ... }
public HttpResponseMessage PostContact() { ... }
public HttpResponseMessage PutContact(int id) { ... }
}
如所示,CORS 仅适用于GetContacts()动作方法。
控制器级别启用
我们还可以在特定的控制器上启用 CORS。 我们只需要用[EnableCors]属性装饰控制器,如下:
[EnableCors(origins: "http://localhost:53870", headers: "*", methods: "*")]
public class ContactsController : ApiController
{
public HttpResponseMessage GetContacts() { ... }
public HttpResponseMessage GetContact(int id) { ... }
public HttpResponseMessage PostContact() { ... }
public HttpResponseMessage PutContact(int id) { ... }
}
如您所见,CORS 适用于此控制器中的任何操作。 然而,有时,您可能希望为一个特定操作禁用 CORS。 假设,我们想要禁用PutContact()操作方法的 CORS。 这可以很容易地通过使用[DisableCors]属性装饰PutContact()动作来实现,如下所示:
[EnableCors(origins: "http://chapter09client.com", headers: "*", methods: "*")]
public class ContactsController : ApiController
{
public HttpResponseMessage GetContacts() { ... }
public HttpResponseMessage GetContact(int id) { ... }
public HttpResponseMessage PostContact() { ... }
[DisableCors]
public HttpResponseMessage PutContact(int id) { ... }
}
如果在控制器级别启用了 CORS,并且您希望为控制器中的特定方法禁用 CORS,那么我们可以使用DisableCors属性装饰这些方法,就像前面的代码片段中给出的那样。
全局启用 CORS
有时候,我们可能需要为 Web API 中的所有控制器和操作启用 CORS。 换句话说,我们可能需要在整个 Web API 中启用 CORS。
这可以通过全局启用 CORS 来实现。 我们需要在WebApiConfig文件中传递一个 HTTP 配置中的EnableCorsAttribute实例给EnableCors方法,如下所示:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var cors = new EnableCorsAttribute("http://localhost:53870", "*", "*");
config.EnableCors(cors);
// ...
}
}
这个全局配置可以在控制器或操作级别被覆盖。 优先级的顺序是动作、控制器和全局。
总结
哦吼! 您刚刚学习了如何在我们的 Web API 中启用跨源请求共享(CORS)。
你们了解了 CORS 是什么以及它是如何工作的。
然后您学习了一些配置内容,包括设置允许的起源、HTTP方法、请求头和响应头。
最后,您了解了在跨源请求中传递凭据以及在 Web API 的不同范围中启用 CORS。
华友世纪! 就是这样,人! 现在,我们知道如何通过采用市场上各种技术中的 apt 安全解决方案来保护我们的 Web API。






















































浙公网安备 33010602011771号