本文介紹如何保護API,無需看前邊文章也能明白吧。
預備知識:
http://www.cnblogs.com/cgzl/p/9010978.html
http://www.cnblogs.com/cgzl/p/9019314.html
建立成熟度2級的 API請看這裡:
https://www.cnblogs.com/cgzl/p/9047626.html
https://www.cnblogs.com/cgzl/p/9080960.html
https://www.cnblogs.com/cgzl/p/9117448.html
HATEOAS:https://www.cnblogs.com/cgzl/p/9153749.html
快取和併發: https://www.cnblogs.com/cgzl/p/9165388.html
保護API和其它: https://www.cnblogs.com/cgzl/p/9172603.html
本文所需專案程式碼(右鍵另存, 字尾改為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由三部分組成: header, payload, signature. 形式如下面的虛擬碼: [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方法裡對它進行配置:
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