


摘? 要:就前后端分離的軟件開發(fā)模式而言,保護后端數據接口不被非法調用是十分重要的。令牌作為獲取保護資源的憑證,需要提供過期時間,否則認證功能就失去了意義。針對活躍用戶,需要在有效時間內提供自動登錄功能,可以提升使用體驗。本文研究了OAuth(Open Authorization,一種開放的授權標準)的認證機制,并在ASP.NET Web API框架基礎上,實現身份認證方案,當訪問令牌過期后,增加令牌刷新機制,既能夠改善用戶體驗,也能夠有效保護數據接口。該方案具有通用性,適用于前后端分離的軟件開發(fā)。通過測試,表明了該方案具有有效性和可行性。
關鍵詞:OAuth;身份驗證和授權;前后端分離;刷新
中圖分類號:TP311.11? ? ?文獻標識碼:A
文章編號:2096-1472(2021)-04-34-05
Abstract: For software development model that separates front and back ends, it is very important to protect back-end data interface from being illegally invoked. The token, as a voucher for obtaining protected resources, needs to provide an expiration time, otherwise authentication function will lose its meaning. For active users, automatic login function needs to be provided within valid time to improve user experience. This paper proposes to implement an identity authentication scheme based on the ASP.NET Web API framework after studying an authentication mechanism of OAuth (Open Authorization, an open authorization standard). When the access token expires, token refresh mechanism is added, which can not only improve user experience, but also effectively protect data interfaces. The proposed scheme is versatile and suitable for software development with separation of front and back ends. Tests show its effectiveness and feasibility.
Keywords: OAuth; authentication and authorization; separation of front and back ends; refresh
1? ?引言(Introduction)
隨著Web應用的快速發(fā)展,前后端分離的開發(fā)方式成為主流趨勢。圖1是一種主流的前后端分離的交互模型,使用前按需設計數據接口,后端(主要是指服務器端)可以為前端(主要是指PC端平臺、APP及各類小程序)提供HTTP(Hyper Text Transfer Protocol, 超文本傳輸協議)服務,而不需要關注業(yè)務的詳細展示[1]。此外,前端主要負責接收和組織展示后端傳過來的數據,根據具體的業(yè)務進行頁面邏輯的設計。這種前后端交互模型結構簡潔,方便前后端開發(fā)者關注自己的業(yè)務,降低了前后端的耦合性,可成功解耦,為后續(xù)開發(fā)工作打下了堅實的基礎。
然而,使用前后端分離的軟件開發(fā)模式,需要關注后端數據接口的安全性,保護接口不被非法調用。針對這種情況,本文實現了基于OAuth(Open Authorization,一種開放的授權標準)2.0的身份驗證和授權方案,一方面能夠保護接口的安全性;另一方面,能夠在令牌過期之后實現刷新機制,改善用戶體驗,并通過Postman工具進行完整測試,表明方案的可行性。
2 身份驗證與授權(Authentication and authorization)
2.1? ?身份驗證
身份驗證是驗證一個系統(tǒng)實體或系統(tǒng)資源具有某種屬性值的過程。對于HTTP而言,客戶端和服務器端是兩個獨立的系統(tǒng)實體,因此,需要從兩個方面去考慮。一方面,若想實現服務器端的身份驗證,就得需要向客戶端保證,請求消息只會發(fā)送給正確的源服務器,在這種情況下,消息的發(fā)送者需要在發(fā)送消息之前核實消息的接收者,通常是認證傳輸連接的另一端。另一方面,若想實現客戶端的身份驗證,就得需要客戶端向服務器端發(fā)送驗證請求,即判斷請求消息是否應該得到授權[2]。常用的身份驗證包含基于密碼的HTTP基礎身份驗證和基于令牌的身份驗證。
2.1.1? ?基于密碼的HTTP基礎身份驗證
基于密碼的HTTP基礎身份驗證,就是將用戶名和密碼信息進行加密處理,生成Ticket(票據),客戶端請求的時候需要校驗Ticket[3],主要步驟如下:
(1)使用冒號作為分隔符,連接用戶名和對應的密碼,使用Base64對連接字符串進行加密處理。
(2)將加密之后的字符串放在Authorization標頭的方案標識符Basic之后。
(3)若通過后臺校驗,客戶端將會接收Ticket,并保存為驗證信息,之后的每一次請求都需要攜帶,以方便服務器進行驗證。驗證成功,則獲取相關信息,否則返回未通過的提示信息。
2.1.2? ?基于令牌的身份驗證
基于令牌的身份驗證,就是使用安全令牌進行驗證。安全令牌是指用于在身份驗證過程中驗證用戶標識的數據對象[4]。我們在Web應用程序中使用的身份驗證Cookie,就是基于令牌的身份驗證,其流程如下:
(1)使用密碼進行初始認證操作,之后會生成一個Cookie,將其返回給客戶端。
(2)客戶端之后發(fā)出的每個請求,都通過這個Cookie進行身份驗證,不再需要初始的身份信息。
2.1.3? ?身份驗證小結
基于密碼的HTTP基礎身份驗證方法存在如下一些問題:
(1)客戶端必須保存密碼,同時,由于密碼采用明文保存,或使用一種可逆的保存方式,增加了密碼泄露的風險。
(2)服務器對每個請求都進行密碼驗證,會增加很多開銷,影響效率。
(3)為防御字典攻擊而使用相關技術,會導致驗證過程計算開銷較大。
(4)不適合分布式應用。分布式系統(tǒng)中,身份驗證通常委托給外部系統(tǒng)。
基于如上考慮,本方案將使用基于令牌的認證方案。
2.2? ?授權
2.2.1? ?授權簡介
授權,可以理解為控制主體對受保護資源進行相應的操作[5]。通常來說,授權問題的核心主要是主體、操作和資源三者的關系,即主體是否可以操作資源[6],如圖2所示。
在Web API中,對于授權我們可以有如下理解:
(1)資源,指HTTP資源,而HTTP資源是請求消息要訪問的資源。
(2)操作,指HTTP方法,包括GET請求、POST請求等。
(3)主體,指執(zhí)行HTTP請求的HTTP客戶端。
2.2.2? ?OAuth 2.0簡介
OAuth 2.0是OAuth 1.0協議的升級版本[6],它的主要目標是讓客戶端的開發(fā)工作變得簡單與便捷,為Web應用程序、手機APP、桌面軟件等提供特定的授權方式。OAuth 2.0允許第三方應用程序獲得HTTP服務的訪問權限,同時,可以通過HTTP服務提供方授權資源擁有者,允許第三方應用程序憑借自己的客戶端憑證獲取在資源服務器上的受保護資源的訪問權限。
在OAuth 2.0中,定義了授權碼模式、簡化模式、密碼模式和客戶端模式四種授權方式[7],綜合考慮本方案的需求,決定采用密碼模式。密碼模式的流程如圖3所示。
這種模式的流程如下:
(1)用戶通過提供賬號和密碼來使用客戶端。
(2)客戶端將賬號和密碼發(fā)送給認證服務器,即請求token。
(3)通過認證服務器驗證,將會向客戶端提供訪問token。
(4)客戶端使用token向資源服務器請求資源。
3 身份驗證和授權方案的設計與實現(Design and implementation of identity verification and authorization scheme)
3.1? ?身份驗證和授權方案的設計
在ASP.NET Web API框架上,使用微軟開發(fā)的基于OWIN規(guī)范的Katana項目中的中間件,能夠很方便地完成基于OAuth 2.0的身份驗證和授權。雖然OAuth 2.0關注的主要內容是授權,但是在該中間件中OAuth 2.0可以提供基于令牌的身份驗證功能[8],其處理訪問令牌的流程如圖4所示。
(1)獲取令牌。如果HTTP請求存在Bearer的Authorization標頭,那么將該標頭作為訪問令牌,否則身份驗證方法不返回任何身份信息。
(2)獲取身份認證票據。解析訪問令牌,可以獲取身份驗證票據信息以及屬性信息。
(3)驗證身份票據,并檢查其有效性。
3.2? ?身份驗證和授權方案的實現
(1)使用Postman發(fā)送仿真模擬請求,并嘗試使用GET方式訪問已有的數據接口,其結果如圖5所示。此時,我們是在沒有限制的情況下獲取相關數據信息。對于我們的方法,如果不加以限制,那么任何客戶端都可無條件訪問我們的接口獲取數據,這樣是不安全的,必須對接口進行限制,只有通過驗證的用戶才可以獲取數據信息。
(2)在項目中,新建一個MyAuthorizationServerProvider
類,用于實現身份驗證,并且重寫ValidateClientAuthentication
方法和GrantResourceOwnerCredentials方法。
關鍵代碼如下:
public class MyAuthorizationServerProvider :
OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(
OAuthValidateClientAuthenticationContext context)
{
context.Validated();
return Task.FromResult
}
public override async Task GrantResourceOwner
Credentials(
OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.
Add("Access-Control-Allow-Origin", new[] { "*" });
AccountService accService = new AccountService();
string md5Pwd = LoginHelperMD5CryptoPasswd
(context.Password);
IList
if (ul.Count() == 0)
{
context.SetError("授權無效", "用戶名或密碼錯誤!");
return;
}
var identity = new
ClaimsIdentity(context.Options.AuthenticationType);
context.Validated(identity);
}
}
為上述數據接口添加Authorize標簽,達到過濾的目的,進而實現對接口的保護。此時,我們再次使用Postman進行仿真請求,嘗試請求接口,返回信息如圖6所示,即顯示已拒絕為該請求授權。
(3)當前接口已經被保護了,我們在訪問接口的時候,就得通過Access_token。因此,我們必須拿到Access_token的值,其獲取方法如下:發(fā)送POST請求,訪問token的URL;發(fā)送請求之前,在Body內添加grant_type、username、password這三個參數,由于我們采用密碼模式進行授權,因此,grant_type的參數是password,如圖7所示,我們發(fā)送一個POST請求之后,響應的信息包含access_token、token_type等值,響應詳情如圖8所示。
(4)我們重新訪問受限的接口,在發(fā)送GET請求的時候,如圖9所示。在請求的標頭里將Authorization設置為KEY,此時需要使用上一步獲取到的Access_token,將其作為VALUE參數的一部分。最終,VALUE對應的值應該為Bearer+access_token,如圖9所示,我們能夠順利獲取到數據信息。這種情況下,15分鐘內客戶端在訪問服務器端的時候,無須進行驗證就可使用該token訪問受保護的資源。
(5)當Access_token的值失效時,我們就需要重新獲取新的令牌值。此時,我們需要在項目目錄中新建一個MyRefreshTokenProvider類,用于令牌到期之前進行刷新,以便獲取新的令牌,并重寫Create方法和Receive方法,代碼如下:
public class MyRefreshTokenProvider : Authentication
TokenProvider
{
private static ConcurrentDictionary
//用于生成refresh_token
public override void Create(AuthenticationToken
CreateContext context)
{
context.Ticket.Properties.IssuedUtc = DateTime.UtcNow;
context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddDays(60);
context.SetToken(Guid.NewGuid().ToString("n"));
_refreshTokens[context.Token] = context.SerializeTicket();
}
//由 refresh_token 解析成 access_token
public override void Receive(AuthenticationTokenReceiveContext context)
{
string value;
if (_refreshTokens.TryRemove(context.Token, out value))
{
context.DeserializeTicket(value);
}
}
}
(6)修改好相關代碼之后,我們需要按照第(4)步的方式重新獲取Token,因為這次獲取數據的時候,會額外獲取到Refresh_token的值,如圖10所示,該值將用于重新獲取新的令牌,而不需要重新使用密碼的授權方式獲取。
(7)當Token過期后,我們需要使用上次得到的Refresh_token重新獲取令牌,即發(fā)送POST請求訪問獲取令牌的URL。發(fā)送請求的時候,需要在Body內添加grant_type、refresh_token這兩個參數,由于本次需要使用刷新令牌的方式獲取新的令牌,因此Grant_type的值為Refresh_token,而Refresh_token的值就是我們在第一次獲取令牌的時候里面的Refresh_token,如圖11所示。
只要在客戶端進行相關設置,即可實現在不影響用戶操作體驗的情況下動態(tài)延長token有效期限,有效地解決了token失效問題。
4? ?結論(Conclusion)
本文通過研究身份驗證和授權的機制,改進了基于令牌的認證方案,增加刷新令牌,能夠在訪問令牌過期之后,使用刷新令牌實現自動登錄和授權,不僅提升了用戶體驗,還能有效地保護后端數據接口的安全性;并使用Postman進行接口測試,驗證了方案的可行性,為前后端分離的身份驗證和授權提供了有效解決方案。
參考文獻(References)
[1] 程冬梅,王瑞聰,劉燕,等.基于REST架構風格的物聯網服務平臺研發(fā)[J].計算機工程與應用,2012,48(14):74-78.
[2] 劉文元.基于ASP.NET Web API的摘要身份驗證改進算法[J].電腦迷,2017(4):56.
[3] GLENN B.ASP.NET Web API設計[M].北京:人民郵電出版社,2015:331-333.
[4] 蔣金楠.ASP.NET Web API2框架揭秘[M].北京:電子工業(yè)出版社,2014:591-594.
[5] 王仲洲,楊曉洪,王劍平,等.基于REST風格的WEB API架構研究[J].微處理機,2016,37(5):52-55.
[6] 沈海波,陳強,陳勇昌.基于OAuth 2.0擴展的客戶端認證方案[J].計算機工程與設計,2017,38(2):350-354.
[7] 王婷婷,趙松澤.基于OAuth 2.0協議的安全授權模型研究[J].軟件工程,2017,20(01):55-59.
[8] DAVIS J, RAJASREE M S. A framework for the description, discovery and composition of RESTful semantic web services[C]. Computational Science, Engineering and Information Technology, 2012:88-93.
作者簡介:
胡獻宇(1994-),男,碩士,講師.研究領域:軟件開發(fā).