YbSoftwareFactory 代碼生成插件【十三】:Web API 的安全性


    ASP.NET Web API 可非常方便地創建基於 HTTP 的 Services,這些服務可以非常方便地被幾乎任何形式的平台和客戶端(如瀏覽器、Windows客戶端、Android設備、IOS等)所訪問,它可根據請求類型自動提供 JSON、XML 等類型的響應內容。在移動互聯網逐漸成為主流的背景下,通過 Web API 對外發布基於標准、通用 HTTP 協議的服務來交換數據無疑具有非常大的優勢和吸引力。本文將主要圍繞 ASP.NET Web API 的安全性進行討論。

一、Forms Authentication

    Forms 認證基於憑據(Ticket)機制,憑據在登錄時創建,通常會寫入到 cookie 中。Forms 認證可應用在任何類型的ASP.NET 應用程序中,例如:WebForms,MVC,甚至 Web API等。默認的配置是 <authentication mode="None" />,因此為了使用Forms Authentication,通常需要在配置文件中進行配置。

    盡管 Forms 認證是 Web 應用程序的首選認證方式,但從 Web API 的安全性來說,其實它並不是一個理想的解決方案,對於非瀏覽器的客戶端來說,做個比喻,就像是穿西裝戴斗笠般不搭調。

    最大的問題是在ASP.NET Web API中使用 Forms 認證方式的時候,並不會返回一個 302 代碼(注:302 表示重定向,Web 應用程序中會被自動導航至設定的登錄頁面地址),在 Web API 中使用 Forms 認證方式可參考如下代碼:

    1)在 WebApiConfig 文件中修改默認的代碼如下:

public  static  class WebApiConfig
{
public  static  void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name:  " DefaultApi ",
routeTemplate:  " api/{controller}/{id} ",
defaults:  new { id = RouteParameter.Optional }
);
config.Filters.Add( new AuthorizeAttribute());
}
}

    2)修改 MVC 的 FilterConfig 默認代碼如下:

public  class FilterConfig
{
public  static  void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add( new HandleErrorAttribute());
filters.Add( new AuthorizeAttribute());
}
}

    通過上述設置,對非授權的資源進行訪問時將返回一個 401 代碼(注:401 表示未經授權的請求),這和在 MVC 中非授權的請求返回到登錄頁面的含義是一樣的。

有童鞋可能會問,為什么不能重定向到登錄頁面呢?其實真正的問題是 Forms 認證本身,它的原理決定了它是面向 Web 應用程序的,它有 Cookie 和重定向等的支持,而 ASP.NET Web API 卻是無狀態的 RESTful 服務,這自然是不適合的。

    說到這里,休息一下,溫習下 HTTP 響應的狀態代碼。

    HTTP 響應的狀態代碼為三位數字的編號,其中第 1 位定義了狀態代碼的類別:1 開頭的代表信息類、2 開頭的代表操作成功類、3 開頭的代表重定向類、4 開頭的代表客戶端一側的請求錯誤類、5 開頭的代表服務器端一側的錯誤類。

    常見的狀態代碼如下:

  • 200 OK
請求已成功,請求所希望的響應頭或數據體將隨此響應返回。
  • 201 Created
請求已經被實現,而且有一個新的資源已經依據請求的需要而建立,且其 URI 已經隨 Location 頭信息返回。假如需要的資源無法及時建立的話,應當返回'202 Accepted'。
  • 204 No Content
服務器成功處理了請求,但不需要返回任何實體內容,並且希望返回更新了的元信息。響應可能通過實體頭部的形式,返回新的或更新后的元信息。如果存在這些頭部信息,則應當與所請求的變量相呼應。
如果客戶端是瀏覽器的話,那么用戶瀏覽器應保留發送了該請求的頁面,而不產生任何文檔視圖上的變化,即使按照規范新的或更新后的元信息應當被應用到用戶瀏覽器活動視圖中的文檔。
由於204響應被禁止包含任何消息體,因此它始終以消息頭后的第一個空行結尾。
  • 400 Bad Request
由於包含語法錯誤,當前請求無法被服務器理解。除非進行修改,否則客戶端不應該重復提交這個請求。
  • 401 Unauthorized
當前請求需要用戶驗證。該響應必須包含一個適用於被請求資源的 WWW-Authenticate 信息頭用以詢問用戶信息。客戶端可以重復提交一個包含恰當的Authorization 頭信息的請求。如果當前請求已經包含了 Authorization 證書,那么 401 響應代表着服務器驗證已經拒絕了那些證書。如果 401 響應包含了與前一個響應相同的身份驗證詢問,且瀏覽器已經至少嘗試了一次驗證,那么瀏覽器應當向用戶展示響應中包含的實體信息,因為這個實體信息中可能包含了相關診斷信息。參見 RFC 2617
  • 403 Forbidden
服務器已經理解請求,但是拒絕執行它。與401響應不同的是,身份驗證並不能提供任何幫助,而且這個請求也不應該被重復提交。如果這不是一個HEAD請求,而且服務器希望能夠講清楚為何請求不能被執行,那么就應該在實體內描述拒絕的原因。當然服務器也可以返回一個 404 響應,假如它不希望讓客戶端獲得任何信息。
  • 404 Not Found
請求失敗,請求所希望得到的資源未被在服務器上發現。沒有信息能夠告訴用戶這個狀況到底是暫時的還是永久的。假如服務器知道情況的話,應當使用 410 狀態碼來告知舊資源因為某些內部的配置機制問題,已經永久的不可用,而且沒有任何可以跳轉的地址。404 這個狀態碼被廣泛應用於當服務器不想揭示到底為何請求被拒絕或者沒有其他適合的響應可用的情況下。
  • 500 Internal Server Error
服務器遇到了一個未曾預料的狀況,導致了它無法完成對請求的處理。一般來說,這個問題都會在服務器的程序碼出錯時出現。

二、身份(Identify)管理

    1、認證(Authentication)和授權(Authorization)

    1)認證的方式

    身份認證的方式主要有如下三個方面:

  • 根據用戶知曉的東西,例如密碼、PIN等
  • 根據用戶擁有的東西,例如證書、U盾等
  • 根據用戶的生物特征,例如指紋、DNA等

    典型的身份認證方式可以為上述的其中一種,其中第一種使用最為普遍,如果一個應用程序需要考慮更高的安全性,需要至少使用上述列表中的兩種身份認證方式。例如銀行卡,本身就屬於第二種認證,但在ATM上取錢時往往還需要密碼,這又使用了第一種認證方式。

    2)基本角色的安全

    在企業級的應用中,基於角色的安全是最常見的安全模型。在.NET Framework中提供了 Identify 對象,一個 Identify 對象代表了某個用戶,最重要就是這兩個接口:IIdentity 和 IPrincipal,這兩個接口提供了基於角色的訪問控制,其中 IIdentify 代表用戶的身份標識,IPrincipal 代表用戶關聯的身份和角色。IPrincipal 有一個 Identity 屬性和 IsInRole(string) 的方法,該方法判斷當前用戶是否屬於某個角色。

    在 .NET 中,每個線程都有一個類型為 IPrincipal 的 CurrentPrincipal 屬性。通常,在身份認證通過后需要創建一個 Principal 對象並賦予主線程的 CurrentPrincipal 屬性,任何新創建的線程會自動創建同樣的 Principal 對象。其實在.NET Framework中已經實現了兩種類型的 Principal。

  • GenericIdentity 和 GenericPrincipal,用於自定義的場合。
  • WindowsIdentity and WindowsPrincipal,用於基於 Windows 認證的場合。

    當然,你也可以自己實現符合自身要求的 Identity 和 Principal。例如在CSLA.NET中就實現了一個自定義的 Identity 和 Principal,可以設置 Serialized 特性,該 Principal 對象可以通過服務從客戶端傳到服務器端進行身份的認證和授權處理,以滿足其 N-Tier 部署下進行身份認證和授權的需要,通過繼承,甚至還可以新增必要的屬性,非常方便。

    3)基本聲明(Claims)的安全

    典型的基於聲明(Claims)的 Identity 如下:

  • 用戶的姓名是誰
  • 用戶的電子郵件是什么
  • 用戶的性別
  • 用戶的年齡是多少
  • 該用戶被允許創建新用戶

    和前面提到的安全模型相比在基於聲明的安全模型,聲明的值必須來自於應用程序所信任的某個實體或機構(通常用戶是/不是什么通過第三方驗證,可以/不可以做什么由應用程序自身確定)

    基於聲明(Claims)的安全模型往往更加貼近現實生活,可以簡化某單個應用程序的身份驗證邏輯,因為這些應用程序不用再重復提供諸如創建賬戶、密碼、密碼重置等機制(注:這些邏輯統一由第三方應用程序完成)。

    同時聲明(Claims)的安全模型也不必要求某個用戶多次登錄到多個應用程序,大大簡化了用戶的身份驗證過程。

    因此,聲明(Claims)的安全非常適合於諸如OAuth等第三方授權的方式和雲計算環境。

    基於聲明(Claims)的安全的實例代碼如下:

static  void Main( string[] args)
{
var claims =  new List<Claim>()
{
new Claim(ClaimTypes.Name,  " Yellbuy "),
new Claim(ClaimTypes.Email,  " yb@yellbuy.com "),
new Claim(ClaimTypes.Role,  " 系統管理員 "),
new Claim(ClaimTypes.Role,  " 系統操作員 ")
};
var id =  new ClaimsIdentity(claims,  " Dummy ");  //  Non-empty string is needed as authentication type
var principal =  new ClaimsPrincipal( new[] { id });
Thread.CurrentPrincipal = principal;
CreateUser();  //  Call the method that needs authorization
}
[PrincipalPermission(SecurityAction.Demand, Role =  " 系統管理員 ")]  //  Declarative
private  static  void CreateUser()
{
new PrincipalPermission( null" NewUser ").Demand();  //  Imperative
Console.WriteLine(Thread.CurrentPrincipal.IsInRole( " 系統管理員 "));
Console.WriteLine( " 用戶已創建 ");
}

    很明顯,用戶是/不是什么通常由第三方確定,因此很多第三方提供了所謂的安全令牌(Security Token)服務。目前有三種標准的令牌(Token)格式,它們是:SAML(安全斷言標記語言)、SWT(簡單 Web 令牌)、JWT(JSON Web 令牌)。三種令牌格式對比如下:

 

SAML

SWT

JWT

表現形式

XML

HTML Form encoding

JSON

處理方式

SOAP

REST

REST

直接支持WIF

協議

WS-Trust and WS-Federation

OAuth 2.0

OAuth 2.0

通常的載體

HTTP body or URL

HTTP Auth header (Bearer)

HTTP Auth header (Bearer)

支持簽名

,非對稱密鑰-X509證書

, HMAC SHA-256 (使用對稱密鑰)

,支持對稱密鑰和非對稱密鑰

    2、Basic Authentication

    在Web Api中不能不提到 Basic Authentication。Basic Authentication 是 HTTP 規范的一部分,它非常的基礎和簡單。主要工作原理如下:

  • 客戶端向服務器發送資源請求。
  • 假如資源請求需要進行身份認證,則服務器發送回一個 401 代碼  - 未經授權響應狀態碼響應頭 WWW-Authenticate: Basic這個響應報頭還可以包含一個字符串,它是服務器所需要的一個有效的憑據來進行后續成功處理請求的唯一標識
  • 現在客戶端發送授權請求信息,例如:WWW-Authenticate: Basic YeMfc1mgUdV2cMj0U0Kjp2C=授權請求頭僅僅是一個用戶ID和密碼進行base64編碼后字符串,然后使用一個冒號進行分割並且不以任何方式加密
  • 假如憑據有效,則服務器返回 200 響應狀態碼。

    因為 Base Authentication 的安全性較差,但對於無 Cookie 的 Web Api 來說,應用上非常的簡單和方便。

    Base Authentication 最大的缺點是憑據會被瀏覽器緩存——直到你關閉瀏覽器為止。如果你已經對某個URI獲得了授權,瀏覽器就會在授權頭發送相應的憑據,這使其更容易受到跨站點請求偽造(CSRF)攻擊

    Base Authentication 通常需要使用HTTPS方式進行加密處理。

三、YbRapidSolution for MVC 中的 Authentication

    在 YbRapidSolution for MVC 的安全解決方案中,采用了 Base Authentication 和 Form Authentication 相互補充的處理方式,使用 Attribute 方式進行請求的 Authentication 處理。Base Authentication 主要面向非Web應用的處理請求,Form Authentication 則對 MVC 的 Controller 的請求進行處理,核心代碼如下:

private  bool AuthorizeRequest(HttpRequestMessage request)
        {
             // 匿名用戶的權限驗證
            AuthenticationHeaderValue authValue = request.Headers.Authorization;

             // Base Authenticated 是否無效
             var isNotValidatedBaseAuthenticated = authValue ==  null
                                        ||  string.IsNullOrWhiteSpace(authValue.Parameter)
                                        ||  string.IsNullOrWhiteSpace(authValue.Scheme)
                                        || authValue.Scheme.Equals(BasicAuthResponseHeaderValue);
             // 客戶端授權標記 有效,則創建Principal並附加到HttpContext.Current.User
             if (!isNotValidatedBaseAuthenticated)
            {
                 string[] parsedHeader = ParseAuthorizationHeader(authValue.Parameter);
                 if (parsedHeader !=  null)
                {
                    IPrincipal principal =  null;
                     if (TryCreatePrincipal(parsedHeader[ 0], parsedHeader[ 1],  out principal))
                    {
                        HttpContext.Current.User = principal;
                    }
                }
            }

             // HttpContent未授權,則檢查匿名用戶的權限
             if (!HttpContext.Current.User.Identity.IsAuthenticated)
            {
                 string roleKey =  string.Format(CacheKeyList.PERMISSION_ROOT_BY_ROLE_KEY,  " EveryOne ");
                 var permissionKeys = _cacheManager.Get(roleKey, () =>
                {
                     var permissionsOfEveryOne = PermissionApi.GetPermissionsInRole( " EveryOne ");
                     if (permissionsOfEveryOne ==  null || permissionsOfEveryOne.Length ==  0)
                         return  new  string[] { };
                     var list = permissionsOfEveryOne.Select(c => c.PermissionKey).ToArray();
                     return list;
                });

                 return CheckPermission(request, permissionKeys);
            }
            
             // 未設置權限Key,則任何已授權用戶均可訪問
             if ( string.IsNullOrWhiteSpace(PermissionKey))  return  true;

             // 登錄用戶的權限驗證
             string userKey =  string.Format(CacheKeyList.PERMISSION_CHILDREN_BY_USER_KEY, HttpContext.Current.User.Identity.Name);
             var allowPermissionKeys = _cacheManager.Get(userKey, () =>
            {
                 var permissions = PermissionApi.GetPermissionsForUser();
                 if (permissions ==  null || permissions.Length ==  0)
                     return  new  string[] { };
                 var list = permissions.Select(c => c.PermissionKey).ToArray();
                 return list;
            });

             return CheckPermission(request, allowPermissionKeys);
        }
private  string[] ParseAuthorizationHeader( string authHeader)
        {
             string[] credentials = Encoding.ASCII.GetString(Convert.FromBase64String(authHeader)).Split( new[] { ' : '});
             if (credentials.Length !=  2 ||  string.IsNullOrEmpty(credentials[ 0]) ||  string.IsNullOrEmpty(credentials[ 1]))
                 return  null;
             return credentials;
        }
View Code

    從上述代碼中可以看出,在 YbRapidSolution for MVC 首先進行 Base Authentication,如果不通過,繼續進行 Form Authentication;如果 Base Authentication 通過,則創建 Form 下的 Principal 然后按 Form Authentication 的方式進行統一處理,這可以確保任何類型的客戶端都能進行相應的 Authentication 處理,充分發揮出 Web Api 的特性,在為各種類型的客戶端和設備提供 API 支持的同時也提供相應的安全保障。

 

附1:YbRapidSoluton for MVC 在線 Demo 地址:http://mvcdemo.yellbuy.com/

附2:最新發布的 YbRapidSolution for WinForm Demo下載:運行環境-.NET 4.0。服務層部署在 Internet
上,,可直接運行;如需在本地部署,除了安裝數據庫外,就是修改配置文件,這里不再詳述。
附3:最新發布的 YbSoftwareFactory V2 下載,運行環境-.NET 4.0。



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM