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

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

1、Forms Authenticationweb

    Forms 認證基於憑據(Ticket)機制,憑據在登陸時建立,一般會寫入到 cookie 中。Forms 認證可應用在任何類型的ASP.NET 應用程序中,例如:WebForms,MVC,甚至 Web API等。默認的配置是 <authentication mode="None" />,所以爲了使用Forms Authentication,一般須要在配置文件中進行配置。數據庫

    儘管 Forms 認證是 Web 應用程序的首選認證方式,但從 Web API 的安全性來講,其實它並非一個理想的解決方案,對於非瀏覽器的客戶端來講,作個比喻,就像是穿西裝戴斗笠般不搭調。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 服務,這天然是不適合的。cookie

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

    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
服務器遇到了一個不曾預料的情況,致使了它沒法完成對請求的處理。通常來講,這個問題都會在服務器的程序碼出錯時出現。

2、身份(Identify)管理

    一、認證(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 (使用對稱密鑰)

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

    二、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方式進行加密處理。

3、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。

相關文章
相關標籤/搜索