58

用ASP.NET Core 2.1 建立规范的 REST API -

 6 years ago
source link: http://www.10tiao.com/html/391/201806/2654070678/2.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

预备知识: 

 用ASP.NET Core 2.0 建立规范的 REST API -- 预备知识 

 用ASP.NET Core 2.0 建立规范的 REST API -- 预备知识 (2) + 准备项目

建立成熟度2级的 API请看这里:

用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST用ASP.NET Core 2.0 建立规范的 REST API -- DELETE, UPDATE, PATCH 和 Log用ASP.NET Core 2.1 建立规范的 REST API -- 翻页/排序/过滤等

HATEOAS:用ASP.NET Core 2.1 建立规范的 REST API -- HATEOAS

缓存和并发:用ASP.NET Core 2.1 建立规范的 REST API -- 缓存和并发

本文所需项目代码(右键另存, 后缀改为zip): https://images2018.cnblogs.com/blog/986268/201806/986268-20180612151833673-1851218969.jpg

认证和授权

认证/身份验证 Authentication, 是验证想要访问特定资源的人/系统的身份的过程.

授权 Authorization, 是确认已认证的用户拥有足够的权限去做某些事的过程.

打个比喻: 认证是一个人可以进入到房间的权限, 而授权则表明这个人可以在房间内做哪些事.

 

认证的过程可以和应用程序分开并且还可以被其它的服务使用, 但是授权的过程通常是针对某个应用程序, 不同的角色会拥有不同的权限.

 

HTTP协议提供了一个协商访问被保护资源的机制, 下图就是HTTP认证:

标准的认证流程开始于一个访问服务器被保护资源的匿名请求, HTTP服务器随后处理了该请求并决定拒绝让它访问被保护的资源, 因为该请求没有凭据; 随后HTTP Server发送了一个WWW-Authenticate Header回去, 这表示它需要这套认证方案. 然后客户端再次发送请求的时候包含了一个Authorization Header, 它的值符合HTTP Server的认证方案. 当服务器收到这次请求时, 它验证了Authorization Header里的凭据, 并让请求通过了管道.

服务器可以提供多种认证方案, 客户端只需选择其中一种即可, 上图中使用的是Basic 认证方案. 还有其它的认证方案:

  • 匿名 Anonymous 也可以当作是一种方案吧, 就当作是授权给所有人好了

  • Basic 认证方案, 它是一种比较老的方案, n年前经常被使用. 它太简单了, 它的值是含有用户名和密码组成的字符串, 并用冒号(:)连接, 并且编码为Base64字符串. 例如username为dave, 密码为1234, 那么Authorization Header的值就是: Authorization: Basic ZGF2ZToxMjM0.

  • Digest 认证方案, 它作为Basic的代替者出现的. 服务器会给客户端发送一个随机字符串作为一个challenge(盘问, 质疑, 挑战), 这个随机字符串叫做nonce(可以理解为临时生成的字符串). 而客户端通过发送一个带有用户名, 密码, nonce和其它信息的hash来进行认证.

  • Bear 认证方案, 它是最流行也是更安全的认证方案. 它使用Bearer Tokens (承载令牌) 来访问由OAuth 2.0协议保护的资源. 任何拥有bearer token的人都可以访问相关的资源. bearer token的生命周期通常很短, 会过期. 例子: Authorization: Bearer: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c.

  • NTLM认证方案, 它是NTLAN Manager的缩写, 它是一种挑战--响应的方案, 要比Digest更安全. 这种方案使用Windows凭据来转化盘问的数据, 而不是使用编码的凭据.

  • Negotiate 认证方案, 它会自动选择NTLM方案和Kerberos协议中的一个, Kerboros协议比NTLM快.

后两种方案都仅限于Windows系统.

这几种方案里Basic提供的保护程度/级别最低, 而Negotiate最高/强.

ASP.NET Core可选择的认证提供商就很多了, 例如ASP.NET Core Identity. 但是它主要用于包含页面的web应用, 例如MVC或Razor Page, 并不适用于REST/Web API, 所以不介绍它了.

如果应用部署在云上, 可以使用Azure Active Directory(AAD) 和 Azure Active Directory B2C (Azure AD B2C). 我没用过, 就不介绍了.

第三方的认证提供商有很多: AspNet.Security.OpenIdConnect.Server(ASOS), IdentityServer4, OpenIddict, Pwdless.....

我一直在用Identity Server 4, 但是这里不会深入介绍, 这里主要介绍如何实现REST API, 如果有需要的话, 可以写一系列关于Identity Server 4的文章.

 

选项很多, 但是要实现的话还需要了解JSON Web Tokens (JWT), 它是一个基于JSON的开放工业标准, 它用于为双方表示一些声明. 它提供了一种紧凑的, 自包含的方式在双方之间用JSON对象来传输信息.

JWT使用 HMAC secret 或 RAS公有和私有键对(key pair) 这两种方式来进行签名.

JWT由三部分组成: headerpayloadsignature. 形式如下面的伪代码: [X=base64(header)].[Y=base64(payload)].[signature([X].[Y])] .

去这个网址可以更直观的理解这三部分: jwt.io

JWT token最终是一个字符串, 它的三个部分用点(.)分开, 前两部分(header payload)是Base64编码的字符串; 最后一部分是前两个Base64字符串的组合, 也是用点(.)分开并进行了签名, 如下图:

 

 使用Bearer方案和JWT的流程如下:

 

配置项目, 在Startup的ConfigureServices里:

如果使用Identity Server 4的话, 这里就可以不这样写了.

首先我们配置使用Bearer认证方案, 然后通过AddJwtBearer设定一些参数. Configuration里面的值可以放在appSettings.json里面或者其它地方:

然后在Configure方法里调用app.UseAuthentication()方法, 要在app.UseMvc()之前调用:

最后使用[Authorize]属性标签把CountryController保护起来, 也可以应用于Action级:

发送不带Authorization Header的请求来测试:

返回 401 Unauthorized 未授权.

返回的Header里面告诉我们应该使用Bearer认证方案.

 

下面我们需要一个可以生成JWT token的节点, 针对本文我就在本项目里建立这个节点吧:

请求token的地址是 /api/authentication, 请求token用的是Basic方案, Post方法里就是先解码, 验证用户名和密码, 成功后调用GenerateToken生成token.

那就按要求再次发送请求:

注意这里usename:password的base64编码是: dXNlcm5hbWU6cGFzc3dvcmQ=

现在我获得了token, 然后我用token再次请求Country资源:

资源就可以正常的访问了.

 

想要解析这个token, 需要到jwt.io:

箭头处需要填上secret.

 

这个例子比较简单, 实际应用中还是使用Identity Server 4之类的东西吧.

 

使用HTTPS

根据官方文档(https://docs.microsoft.com/en-us/aspnet/core/security/enforcing-ssl?view=aspnetcore-2.1&tabs=visual-studio#require-https), 它建议ASP.NET Core web应用都应该调用HTTPS重定向中间件, 这样就可以把所有的HTTP请求转换为HTTPS.

只需要在Startup的Configure方法里调用UseHttpsRedirection()方法即可:

而在ConfigureServices方法里可以配置这个中间件:

 

HSTS (HTTP 严格的传输安全协议)

web应用通过使用特殊的响应header可以选择使用加强的安全协议OWASP(Open Web Application Security Project), HSTS(HTTP Strict Transport Security). 当所支持的浏览器接收到这个header的时候, 浏览器就会阻止任何通过HTTP到指定域名的通信, 会使用HTTPS代替. 同时它也会阻止从浏览提的提示框点击的HTTPS.

为实现这个只需要在Startup的Configure里使用:

 

 一般不建议在开发环境使用Hsts, 因为浏览器极有可能会缓存HSTS 的header. 默认情况下, UseHsts会排除本地回路的地址.

UseHsts会排除下列回路宿主:

  • localhost : IPv4 回路地址.

  • 127.0.0.1 : IPv4 回路地址.

  • [::1] : IPv6 回路地址.

可以在ConfigureServices方法里对它进行配置:

这部分具体请查看文档: https://docs.microsoft.com/en-us/aspnet/core/security/enforcing-ssl?view=aspnetcore-2.1&tabs=visual-studio#http-strict-transport-security-protocol-hsts

 

CORS 跨域请求

配置注册CORS需要在Startup的ConfigureServices方法完成:

针对整个应用启用CORS需要在Configure方法里调用下面的方法:

应该尽早的调用该方法, 以便在它后边注册的节点都可以被跨域访问.

这是第一种方法, 使用的是lambda表达式.

注意URL地址结尾不要有/, 它会引起错误.

这种方法使用的是CorsPolicyBuilder 类, 它拥有Fluent API, 可以串接方法调用:

 

第二种方法是使用策略.

在ConfigureServices里配置好命名的策略:

在Configure方法里使用该策略:

另外也可以不适用UseCors(), 而是在下面这几种级别指定使用该策略:

Action级别:

Controller级别:

全局Controller级别:

这么用的话, 需要禁用CORS策略就:

 

关于CORS的具体配置, 还是请参考官方文档: https://docs.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-2.1

 

Rate Limiting 速率限制

速率限制是指限制被允许的请求到API(或某个特定的资源)。这样就可以保护API,避免一些非正常使用的场景,例如网络爬虫或请求太多而导致API的性能严重下降,Dos和DDos。针对这点我们采取的节流策略是控制允许访问API的请求的频率/速率,它可以决定特定的请求是否被允许。

例如客户端只允许每小时有100个请求到达API,也可以按天计算,还可以带着IP地址一起限制。

响应的Header可以用来表示速率限制,但是这些Header并不是HTTP标准。这些header都以X-Rate-Limit开头。

  • X-Rate-Limit-Limit, 这个表示添加了限制并包含了限制的有效期。

  • X-Rate-Limit-Remaining,表示该客户还剩下多少个被允许的请求。

  • X-Rate-Limit-Reset,提供关于何时限制会被重置的时间信息。

如果达到限制了,这些响应会返回429 Too many requests 状态码。有可能会包含一个Retry-After 响应Header,而响应的body应该包含解释当前状态的细节信息。当然这都是理论上要求的。

 

下面去实现,首先安装这个库 AspNetCoreRateLimit (https://github.com/stefanprodan/AspNetCoreRateLimit):

首先在Startup的ConfigureServices里面注册,用到了MemoryCache:

这里配置的是IP限制,它允许有很多规则,这里我只用了一个:针对所有的资源,每5分钟最多3次请求。

现在,我需要注册一个策略存储和速率限制计数器的存储,这两个是被中间件使用。所以还需要注册这两个服务:

这里都使用的是Singleton单例,因为我们需要的是针对全局的请求来做操作。

接下来要在管道里添加中间件,它应该放在靠前的位置,在日志和异常之后:

 

测试,发送一个请求看结果:

可以看到5分钟内还剩下两次请求的配额。限制重置的时间大约在5分钟之后。

发送请求超限之后,就会返回429:

Retry-After提示了再过294秒后可以再试试。。。

而响应的body是这样提示的:

 

我们再组合几个其它的规则:

现在允许5分钟10次请求,但是每10秒钟最多只能有两次请求。

第一次请求后:

5分钟内还剩9次,然后我10秒内连续发送两次请求,然后再发送一次请求:

这时超出了限制,Header里:

提示6秒后可以重试, 6秒后再次发送请求:

 

这个库还是挺灵活强大的,更多功能还需要看官方文档。

 

API 文档

业界通常会使用Swagger OpenAPI来对RESTful API进行格式化描述,而Swagger OpenAPI的当前版本是v3.

ASP.NET Core有一个第三方库Swashbuckle,它支持Swagger,但是只支持版本2,版本2有个重要的缺陷就是不支持Action重载,之前HATEOAS的文章里提到过我们需要使用这种重载。所以Swashbuckle暂时并不是完全合适,所以我就不装它了。

就暂时不弄自动文档了。。。

 

单元测试

需要使用到xUnit和Moq,这里不介绍了。

关于xUnit,我写过几篇文章,有兴趣可以参考下:

http://www.cnblogs.com/cgzl/p/8283610.html

http://www.cnblogs.com/cgzl/p/8287588.html

http://www.cnblogs.com/cgzl/p/8438019.html

http://www.cnblogs.com/cgzl/p/8444423.html 

Moq的文章博客园应该有,如果需要的话,我可以写一下。

 

其它

其它可能需要了解的包括:POSTMAN/Newman自动化测试,CI,CD,GraphQL等等。我就不介绍了 

这个系列文章就到这了。

源码(我还需要整理一下源码,现在有点乱):https://github.com/solenovex/ASP.NET-Core-2.0-RESTful-API-Tutorial

相关文章:

原文地址: https://www.cnblogs.com/cgzl/p/9165388.html


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK