2010年9月15日 星期三

如何在透過httpwebrequest請求網頁的時候,進行AD驗證

應用程序向IIS傳送身份驗證[轉]
一、 匿名訪問.............................................. .................................................. ........................ 1
1、 客戶端發送一個匿名的http請求........................................ .................................... 2
2、 IIS直接返回請求的頁面內容......................................... .......................................... 2
二、 Windows集成驗證............................................. .................................................. ......... 2
1、 客戶端和服務端機器都在域,客戶端以域帳戶登錄,以服務器名訪問...................... 3
1.1. 第一次HttpWebRequest先以匿名發送http請求....................................... .. 3
1.2. 服務端返回無授權回應.......................................... ........................................... 3
1.3. 再次請求,選擇Kerberos驗證,附帶上此用戶的驗證票................................. 3
1.4. 服務端驗證通過,返回頁面......................................... ..................................... 4
2、 客戶端和服務端機器都在域,客戶端以域帳戶登錄,以服務器名訪問以外的所有情況 5
2.1. 第一次HttpWebRequest先以匿名發送http請求....................................... .. 5
2.2. 服務端返回無授權回應.......................................... ........................................... 5
2.3. HttpWebRequest選擇NTLM驗證,請求質詢碼........................................ .... 5
2.4. 服務器返回質詢碼............................................ ................................................ 5
2.5. HttpWebRequest發送登錄本機的賬戶加密後的質詢碼.................................. 5
2.6. 服務器通過驗證,返回頁面.......................................... .................................... 6
3、 客戶端傳送定制用戶憑據到服務端....................................... .................................... 6
三、 基本身份驗證............................................. .................................................. .................. 6
1.1. 第一次HttpWebRequest先以匿名發送http請求....................................... .. 7
1.2. 服務端返回無授權回應.......................................... ........................................... 7
1.3. HttpWebRequest發送base64格式的用戶名和密碼..................................... 7
1.4. 服務器通過驗證,返回頁面.......................................... .................................... 7
四、 一個應用程序訪問web services的例子........................................ ................................ 8
1.1. Web services端代碼............................................ .......................................... 8
1.2. Soap調用web services的格式.......................................... ........................... 8
1.3. 客戶端應用調用web services代碼......................................... ........................ 9


前面那篇文章《IIS的各種身份驗證詳細測試》討論的是客戶端為IE訪問IIS的各種身份驗證的情況,這篇文章討論一般應用程序如何應對IIS的各種身份驗證,把身份憑據傳送到IIS服務端。
無論是訪問一般的網頁,還是訪問web services,都是通過http協議來完成的,相應的dotnet類庫中提供了HttpWebRequest類來實現http的各種訪問控制。

一、 匿名訪問
服務端IIS設置為允許匿名訪問,客戶端使用HttpWebRequest發送一個get請求,請求一個頁面。
//根據需要訪問的URL新建HttpWebRequest對象
HttpWebRequest myHttpWebRequest = (HttpWebRequest)HttpWebRequest.Create("http://192.168.100.82/iisstart.htm");
myHttpWebRequest.Method = "GET";

//發送http請求,接收回應到HttpWebResponse對象
HttpWebResponse myWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();

//獲得http回應的內容部分,是個Stream類型的對象
Stream newStream = myWebResponse.GetResponseStream();
byte[] byteResponse = new byte[myWebResponse.ContentLength];
newStream.Read(byteResponse, 0, (int)myWebResponse.ContentLength);

//最後把http回應部分內容轉成string
string strContent = Encoding.UTF8.GetString(byteResponse);
myWebResponse.Close();

抓數據包看這個http的請求和回應過程就是這樣的:
1、 客戶端發送一個匿名的http請求
GET /iisstart.htm HTTP/1.1
Host: 192.168.100.82
Connection: Keep-Alive
2、 IIS直接返回請求的頁面內容
HTTP/1.1 200 OK
Content-Length: 200
Content-Type: text/html
Last-Modified: Fri, 23 Nov 2007 07:00:18 GMT
Accept-Ranges: bytes
ETag: "6084ba819e2dc81:32a"
Server: Microsoft-IIS/6.0
MicrosoftOfficeWebServer: 5.0_Pub
X-Powered-By: ASP.NET
Date: Fri, 23 Nov 2007 07:00:44 GMT


......

.This is a sample page!

二、 Windows集成驗證
服務端IIS設置為不允許匿名訪問,只選擇winodws集成驗證,客戶端使用HttpWebRequest發送一個get請求,請求一個頁面。
要請求需要windows集成驗證的資源,前一篇文章已經討論過,客戶端必須選擇NTLM或Kerberos這兩種驗證方式中的一種,傳送客戶端登錄用戶的用戶憑據到服務端進行驗證。
對於NTLM驗證來說,客戶端使用登錄用戶的密碼派生的key加密服務端發來的質詢碼,服務端也對質詢碼做同樣的加密處理,兩端的加密結果一致,則表明驗證通過。
對於Kerberos來說,客戶端只發送登錄用戶的訪問這個IIS的驗證票,這個驗證票只有這個用戶和IIS服務器能識別,IIS只憑客戶的驗證票就可驗證用戶的身份。
HttpWebRequest的Credentials屬性用來承載用戶的用戶憑據。
CredentialCache類的靜態DefaultCredentials 屬性能夠獲取運行當前代碼的用戶系統憑據。
DefaultCredentials 屬性僅適用於基於NTLM、協商和Kerberos 的身份驗證。
DefaultCredentials 表示運行應用程序的當前安全上下文的系統憑據。對於客戶端應用程序,這些通常是運行應用程序的用戶的Windows 憑據(用戶名、密碼和域)。DefaultCredentials可以獲得用於Kerberos驗證的身份驗證票,也可以獲得用於NTLM驗證的當前用戶的用戶名和密碼。
為了在應用程序代碼中加上對windows集成驗證的支持,只需在上述匿名訪問的代碼中給HttpWebRequest對象的賦予用戶系統憑據即可,HttpWebRequest對象會根據實際情況選擇使用哪一種驗證方式,並傳送相應的用戶憑據,在http請求發送出去之前增加以下代碼:
myHttpWebRequest.Credentials = CredentialCache.DefaultCredentials;

Windows集成驗證,HttpWebRequest對象選擇哪種驗證方式,基本會出現兩種情況:
1、 客戶端和服務端機器都在域,客戶端以域帳戶登錄,以服務器名訪問
1.1. 第一次HttpWebRequest先以匿名發送http請求

GET /iisstart.htm HTTP/1.1
Host: logs:81
Connection: Keep-Alive
1.2. 服務端返回無授權回應

HTTP/1.1 401 Unauthorized
Content-Length: 1327
Content-Type: text/html
Server: Microsoft-IIS/6.0
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
X-Powered-By: ASP.NET
Date: Fri, 23 Nov 2007 07:22:32 GMT
1.3. 再次請求,選擇Kerberos驗證,附帶上此用戶的驗證票

HttpWebRequest首選到AD中查詢要訪問的服務器是否在域中,如果是在同一域中,則直接發送Kerberos驗證的用戶驗證票。如果服務器不在域中,則會選擇NTLM驗證。
這裡的情況是雙方都在域中,選擇Kerberos驗證。
GET /iisstart.htm HTTP/1.1
Authorization: Negotiate YIIEzQYGKwYBBQUCoIIEwTCCBL2gJDAiBgkqhkiC9xIBAgIGCSqGSIb3EgECAgYKKwYBBAGCNwICCqKCBJMEggSPYIIEiwYJKoZIhvcSAQICAQBuggR6MIIEdqADAgEFoQMCAQ6iBwMFACAAAACjggOiYYIDnjCCA5qgAwIBBaEOGwxTWkJUSS5HT1YuQ06iFzAVoAMCAQKhDjAMGwRIVFRQGwRsb2dzo4IDaDCCA2SgAwIBF6EDAgEMooIDVgSCA1L3mblv51lhhUk65uuHdDfZVu5MH7BU3VM0Ouk3Nz2omVDTFxoSmcRhyvP3YUWS9sJIvQf74ztvLuEnN3Oevcj p9IfI4YceqfkuYuwm0AUS57PN OGRXr9TFL4 /A0CakEb8v6Hie4OHCoTHTBc9/XuOIn80BmkOip0/Eh4h6pw60MumZHULxeaAOtEI0ECPk4OULeh2W8MeF3iWEOQyFExKY2A1aRZoUhVIbII1G45OYZz03EC869H8z8hzc4fpW6GnHFtneLUAuDusp7ySwMFY5q/xvGu5qygXH86tI/QAkE5qGYgsYM3JjliI6rKFhxcHyqHo IwWs82LszP3o7V2E2iQsIUZqQo08gyKBwQubBNRETPPrJtT22X4YoJOWB8jYfa4vXfT77n6rLg/ps 4w9l5tOD4cmfWicid1gTKpdYbOlspcnSqa/UrlfqtjJglC1xyRXvOQUJkmSnIUuJEh YmDWduCFVq6Zhng7/cKceIUHDrK8f0FNJeIqQ5BzqRK2GIy0NpjPgG/Y1apnYEk9HiDjpIz2olk1U9qui Zp4zekQFEHZp8arRQD5jrvA0Q2qrjgFL1SXZAy OhbL64p4LQUnJFCidhnpzh4ng0GZIxeWbdtkYJEEwQ8zVKPVSLgTeCnCZ7qzqPFOrHTElcuDj6M7lrpJI9VyhEIQiX1fO9oAVYGSlS/kjANumKdmFarPx0apVJajQUFWN9Hx0bGtsP8A9jfZJRkek5VVeb6NLRy QVaaa0gOij/E76R0EdLM8/kk9yts5IeUuBnGLLyzvAIRT8UYhUIpLWIk8qfy1 nvW25YyL810Vawnz AtMBYFXGkXyXJTfc0G xpElOdM8eqPaYTtzJLvDiu0yQNPYKdyny2PcHXtqjIU4EyVru1cyjoJzp2B/URvkK3bgBh696tHGL1OfNeaBfpDZlwkP3juNxn7DenP5jiwq9hunPMjoDghoNEGcHC oLgWdgq7IViUkGuGbHGL96eYN/bPIhFWnEUlNM lSxEYhWHZB/iRSE53crzJN7n0hobDtSUg5c7l/2BqIVsZkqY yzh60uLTmOxNfMguAWdu4YdfqE74EDO2cdMaAq99r93CjLWySbhvKtKVCv9C5MaGpIG6MIG3oAMCAReiga8EgaygGo fZv6Mbh9qTCUKlJ2aY9RZ9VpOnOy4zPIuw6JNHShr1kNckSS/j TRrCfQRaoeoQlhOWFkrkqjtl2GCpvBG3ZcTd7rXHeAV5Tc rd/TADsbya2ZnEx/o5TcTmPe4gauuTFpwL2RTaFzY8oIkGvh0kmOu/M/OvXUiFqHN/ua556UPePQ9qbRM198a1pNXk167QDhZjfT4tsPlkeRf84NfzOayDhcFrLf1g4
Host: logs:81
1.4. 服務端驗證通過,返回頁面

HTTP/1.1 200 OK
Content-Length: 167
Content-Type: text/html
Last-Modified: Wed, 14 Nov 2007 08:21:24 GMT
Accept-Ranges: bytes
ETag: "bf2d54589726c81:e86"
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
WWW-Authenticate: Negotiate oYGgMIGdoAMKAQChCwYJKoZIgvcSAQICooGIBIGFYIGCBgkqhkiG9xIBAgICAG9zMHGgAwIBBaEDAgEPomUwY6ADAgEXolwEWgai3BchBA/s/Vtx44FMiv7n0LcpxmkRlVRhgIhsuijQnu6/qIb0AsMrIU4nHqDlZnLOkVwlXDAIiTumPWfHK V/DmjKF5O6jlJVdFSu9xghGGyYivc3fvCWSQ==
Date: Fri, 23 Nov 2007 07:22:32 GMT




This is a simple page!


2、 客戶端和服務端機器都在域,客戶端以域帳戶登錄,以服務器名訪問以外的所有情況
2.1. 第一次HttpWebRequest先以匿名發送http請求

GET /iisstart.htm HTTP/1.1
Host: logs:81
Connection: Keep-Alive
2.2. 服務端返回無授權回應

HTTP/1.1 401 Unauthorized
Content-Length: 1327
Content-Type: text/html
Server: Microsoft-IIS/6.0
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
X-Powered-By: ASP.NET
Date: Fri, 23 Nov 2007 07:22:32 GMT
2.3. HttpWebRequest選擇NTLM驗證,請求質詢碼

HttpWebRequest判斷不滿足客戶端以域賬戶登錄,並且訪問的服務器也是在域中,HttpWebRequest即選擇NTLM。
GET /iisstart.htm HTTP/1.1
Authorization: Negotiate TlRMTVNTUAABAAAAt4II4gAAAAAAAAAAAAAAAAAAAAAFAs4OAAAADw==
Host: 192.168.100.5:81
2.4. 服務器返回質詢碼

HTTP/1.1 401 Unauthorized
Content-Length: 1251
Content-Type: text/html
Server: Microsoft-IIS/6.0
WWW-Authenticate: Negotiate TlRMTVNTUAACAAAACgAKADgAAAA1goniKBDxpIifbe0AAAAAAAAAAHwAfABCAAAABQLODgAAAA9TAFoAQgBUAEkAAgAKAFMAWgBCAFQASQABAAgATABPAEcAUwAEABgAcwB6AGIAdABpAC4AZwBvAHYALgBjAG4AAwAiAGwAbwBnAHMALgBzAHoAYgB0AGkALgBnAG8AdgAuAGMAbgAFABgAcwB6AGIAdABpAC4AZwBvAHYALgBjAG4AAAAAAA==
X-Powered-By: ASP.NET
Date: Fri, 23 Nov 2007 08:46:23 GMT
2.5. HttpWebRequest發送登錄本機的賬戶加密後的質詢碼

HttpWebRequest通過CredentialCache.DefaultCredentials獲得登錄本機的用戶名和口令,用口令派生的key加密質詢碼發送到服務器。
GET /iisstart.htm HTTP/1.1
Authorization: Negotiate TlRMTVNTUAADAAAAGAAYAGYAAAAYABgAfgAAAAoACgBIAAAACgAKAFIAAAAKAAoAXAAAABAAEACWAAAANYKI4gUCzg4AAAAPUwBaAEIAVABJAGoAaQBuAGoAegBKAEkATgBKAFoA8Le58UfsaJIAAAAAAAAAAAAAAAAAAAAAsTIQTM1MCkPFXRkaNpjW aLQCXsOGnUiDBp33pfcOMLLQJQuAyM/zQ==
Host: 192.168.100.5:81
2.6. 服務器通過驗證,返回頁面

HTTP/1.1 200 OK
Content-Length: 167
Content-Type: text/html
Last-Modified: Wed, 14 Nov 2007 08:21:24 GMT
Accept-Ranges: bytes
ETag: "bf2d54589726c81:e86"
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
Date: Fri, 23 Nov 2007 08:46:23 GMT




This is a simple page!


3、 客戶端傳送定制用戶憑據到服務端
前兩種情況都是傳送客戶端當前登錄用戶的用戶憑據到服務端,但有時會需要客戶端根據不同的情況以服務端的不同的用戶帳號訪問服務端資源。
HttpWebRequest.Credentials屬性不設置為缺省登錄本機的用戶CredentialCache.DefaultCredentials,使用NetworkCredential提供定制的用戶憑據,這樣用法只能使用NTLM驗證。
通過NetworkCredential對象定制一個用戶憑據,像這樣:
myHttpWebRequest.Credentials = new NetworkCredential("用戶名", "口令");

這裡的用戶名用戶名和口令是服務端的用戶,這樣設置以後,HttpWebRequest的行為跟上面第二種情況一樣,也是先以匿名發送請求,收到服務端返回的無授權(要求windows驗證)的http回應後,HttpWebRequest選擇NTLM,其後的過程也跟上面第二種情況一樣。

三、 基本身份驗證
服務端IIS設置為不允許匿名訪問,只選擇了基本身份驗證,客戶端使用HttpWebRequest發送一個get請求,請求一個頁面。
基本身份驗證,客戶端直接把用戶的用戶名和密碼簡單的轉換為base64編碼後發送到服務端,屬於明碼傳送,不具安全性。
基本身份驗證,HttpWebRequest.Credentials屬性只能使用NetworkCredential來傳送用戶名也密碼,CredentialCache.DefaultCredentials不支持基本身份驗證。
在服務端只要求基本身份驗證的情況下,如果HttpWebRequest.Credentials屬性使用的是CredentialCache.DefaultCredentials,當收到服務端返回無授權(要求基本身份驗證)回應後,HttpWebRequest直接跑出無授權異常。
客戶端用戶憑據也是這樣:
myHttpWebRequest.Credentials = new NetworkCredential("用戶名", "口令");
但是,根據服務端不同的回應,HttpWebRequest會有不同的動作。
看一下整個請求響應過程:
1.1. 第一次HttpWebRequest先以匿名發送http請求

GET /iisstart.htm HTTP/1.1
Host: 2003base
Connection: Keep-Alive
1.2. 服務端返回無授權回應

服務端IIS設置為不允許匿名訪問,要求基本身份驗證,所以服務端返回無授權回應,同時在http頭加了個WWW-Authenticate: Basic realm="2003base" 頭,Basic表示要求基本身份驗證。
HTTP/1.1 401 Unauthorized
Content-Length: 1327
Content-Type: text/html
Server: Microsoft-IIS/6.0
WWW-Authenticate: Basic realm="2003base"
MicrosoftOfficeWebServer: 5.0_Pub
X-Powered-By: ASP.NET
Date: Mon, 26 Nov 2007 08:52:42 GMT
1.3. HttpWebRequest發送base64格式的用戶名和密碼

HttpWebRequest收到Basic的驗證頭後,就把NetworkCredential提供的用戶名和口令鏈接起來,轉換成base64編碼,直接發送到服務端。
Base64編碼用戶名和密碼看上去是這樣的:Y2hua2luZzpwYXNzd29yZA==
經過轉換後,可以看到用戶名和密碼是:chnking:password
GET /iisstart.htm HTTP/1.1
Authorization: Basic Y2hua2luZzpwYXNzd29yZA==
Host: 2003base
1.4. 服務器通過驗證,返回頁面

HTTP/1.1 200 OK
Content-Length: 200
Content-Type: text/html
Last-Modified: Fri, 23 Nov 2007 07:00:18 GMT
Accept-Ranges: bytes
ETag: "6084ba819e2dc81:356"
Server: Microsoft-IIS/6.0
MicrosoftOfficeWebServer: 5.0_Pub
X-Powered-By: ASP.NET
Date: Mon, 26 Nov 2007 08:52:42 GMT


......

.This is a sample page!

四、 一個應用程序訪問web services的例子
1.1. Web services端代碼

很簡單的一個Web services,一個foo方法,輸入一個整數,返回一個這個整數乘以十後的字符串,內容如下:
[WebService(Namespace = "http://chnking.com/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class FOOSAMPLEService : System.Web.Services.WebService
{
[WebMethod]
public string foo(int a)
{
return (a*10).ToString();
}
}
1.2. Soap調用web services的格式

發佈為web services後,在瀏覽器瀏覽這個web services的asmx文件,可以看到這個foo如何用soap協議進行調用。
以下是 SOAP 1.2 請求和響應示例。所顯示的佔位符需替換為實際值。
POST /WS1/Service.asmx HTTP/1.1
Host: localhost
Content-Type: application/soap xml; charset=utf-8
Content-Length: length

://www.w3.org/2003/05/soap-envelope">

HTTP/1.1 200 OK
Content-Type: application/soap xml; charset=utf-8
Content-Length: length

://www.w3.org/2003/05/soap-envelope">
string

1.3. 客戶端應用調用web services代碼

下面代碼在應用程序中用HttpWebRequest對像以上面web services要求的格式發送soap請求。服務端設置為只允許windows身份驗證,HttpWebRequest對象同時傳送用戶憑據,最後接收服務端的返回的soap的結果。
//根據需要訪問的URL新建HttpWebRequest對象
HttpWebRequest myHttpWebRequest = (HttpWebRequest)HttpWebRequest.Create("http://localhost:81/ws1/Service.asmx");
//要發送soap請求的內容,必須使用post方法傳送數據
myHttpWebRequest.Method = "POST";
//缺省當前登錄用戶的身份憑據
myHttpWebRequest.Credentials = CredentialCache.DefaultCredentials;

//準備soap請求的內容
string cont;
cont = "";
cont = "='http://www.w3.org/2003/05/soap-envelope'>";
cont = " ";
cont = " ";
cont = " 1";
cont = " ";
cont = " ";
cont = "";

byte[] byteRequest = Encoding.UTF8.GetBytes(cont);

myHttpWebRequest.ContentType = "application/soap xml; charset=utf-8";
myHttpWebRequest.ContentLength = byteRequest.Length;
myHttpWebRequest.Timeout = 100000;

//將soap請求的內容放入HttpWebRequest對象post方法的請求數據部分
Stream newStream = myHttpWebRequest.GetRequestStream();
newStream.Write(byteRequest, 0, byteRequest.Length);

//發送請求
HttpWebResponse myWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();

//將收到的回應從Stream轉換成string
newStream = myWebResponse.GetResponseStream();
byte[] byteResponse = new byte[myWebResponse.ContentLength];
newStream.Read(byteResponse, 0, (int)myWebResponse.ContentLength);

//str裡面就是返回soap回應的字符串了
string str = Encoding.UTF8.GetString(byteResponse);

myWebResponse.Close();
返回的str的結果是:
://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">.com/">10
http://www.cnblogs.com/starspace/archive/2009/04/17/1438369.html

如何透過Request的方式,進行Post網頁,並直接解析該請求網頁的內容

有人的做法是用webClient去做,但是保哥提到透過該方法有以下的缺點:

無法指定 Timeout,所以當網路連不上時,網頁或程式會整個停在那裡很久!
不適合用來下載大量的檔案,高負載的網站也不適合這樣用,即便你用非同步的方式撰寫,也會讓WebClient
因為佔據過多 Threads 而導致效能降低,壹台電腦的 Threads 數是有限制的。

對於較正式的場合,還是建議改用 HttpWebRequest 類別處理。

參考源碼如下:

//指定編碼
Encoding myEncoding = Encoding.GetEncoding("big5");

//宣告要post的變數字串
string param = HttpUtility.UrlEncode("FormID", myEncoding) + "=" + HttpUtility.UrlEncode("ACT011", myEncoding) + "&" + HttpUtility.UrlEncode("SheetNo", myEncoding) + "="+HttpUtility.UrlEncode("200900010001", myEncoding) + "&" + HttpUtility.UrlEncode("ParserRoleID", myEncoding) + "=" + HttpUtility.UrlEncode("1", myEncoding) + "&" + HttpUtility.UrlEncode("BranchNo", myEncoding) + "=" + HttpUtility.UrlEncode("0010", myEncoding) + "&"+ HttpUtility.UrlEncode("SerialNo", myEncoding) + "=" + HttpUtility.UrlEncode("01", myEncoding) + "&" + HttpUtility.UrlEncode("FlowID", myEncoding) + "=" + HttpUtility.UrlEncode("1", myEncoding) + "&"+ HttpUtility.UrlEncode("UserID", myEncoding) + "=" + HttpUtility.UrlEncode("3726", myEncoding);

//對post參數進行編碼
byte[] postBytes = Encoding.ASCII.GetBytes(param);

//宣告HttpWebRequest物件,並指派URL對像
HttpWebRequest req = HttpWebRequest)HttpWebRequest.Create("http://localhost/EFSP7/PostTest.aspx");
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded;charset=big5";
req.ContentLength = postBytes.Length;

發送Request,並寫入Post參數
using (Stream reqStream = req.GetRequestStream())
{
reqStream.Write(postBytes, 0, postBytes.Length);
}

//取得Request的回傳串流
using (HttpWebResponse wr = req.GetResponse() as HttpWebResponse)
{
StreamReader tSR = new StreamReader(wr.GetResponseStream(), System.Text.Encoding.Default);
string tResult = tSR.ReadToEnd();
tSR.Close();

try
{
Response.Write(tResult);
}
catch (Exception ex)
{
throw ex;
}

}




參考網址: