代码改变世界

[C#]使用 HttpWebRequest 瀏覽整合式 windows 認證環境的網站

2009-11-11 18:15  bennieguo  阅读(252)  评论(0编辑  收藏  举报

一般來說,企業為了保密,或是避免員工亂來,
通常在公司電腦有做權限的管理,
常見的就是加入網域,走 Proxy 之類的作法,
員工電腦必須在網域下輸入帳號密碼才能進到 Windows。

上面講的跟主題有啥關係勒?
最近碰到的一個問題就出在這,
看下圖比較快:


 

因為某些需求,當使用者在瀏覽特定頁面的時候,例如頁面A;
會去觸發註冊在頁面A上的一個使用者控制項,(我比較喜歡稱為元件)
然後我們在控制項中加入 HttpWebRequest/Response,
讓程式「偷偷的」去瀏覽頁面B,
之所以會寫「偷偷的」,是因為控制項會去創造類似虛擬瀏覽器的環境,
去瀏覽並接受特定網址回傳的訊息,
由於這個動作是使用者不知情的(幕後執行),所以用「偷偷的」來形容。



網友看到這邊可能會有疑問…那為何頁面B要執行的程式不寫在頁面A就好?
還要如此大費周章?
這部分牽涉到頁面A重複使用性的問題,因為頁面A是同時給很多不同 Case 的使用者去瀏覽的,
而每個Case 要去觸發的狀況又不一樣,所以不能全寫死在程式中。
例如:使用者1瀏覽頁面A時,要幕後觸發的是頁面C,
使用者2瀏覽頁面A時,要幕後觸發的反而是頁面D。



寫完後,那程式應該可以 run 了吧?
但問題總出在令人意料不到的環節上:


 

沒錯,就是有問題!
就在我把頁面A打開的時候,出現了上圖的錯誤訊息,
關鍵就在 HttpWebRequest 所執行的身分以及授權



其實當我們利用 Browser 瀏覽網站時,Browser 已經事先幫我們做了很多事情,
特別是在整合式 windows 認證環境的頁面,如果使用者是有登錄網域的,
則對使用者來說絲毫不會有任何差異,因為Browser 已經幫你完成認證了。
但是 HttpWebRequest 就不一樣了,它是程式創造出來的虛擬瀏覽器,
完全沒有任何權限,這將導致它在瀏覽一些非匿名存取的網站時,就會出現上面的錯誤。



先來看一下程式碼,標準 MSDN 的寫法:

 

01 void MailSenderTrigger(string btNo)
02 {
03 try
04 {
05 string URL = "http://www.urWebName.com/"; //欲瀏覽的特定頁面
06
07 //初始化 HttpWebRequest
08
HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(URL);
09
10 // 設定內容類型
11
myHttpWebRequest.ContentType = "application/x-www-form-urlencoded";
12
13  //初始化 HttpWebResponse
14 HttpWebResponse
myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
15 Stream
msgstream = myHttpWebResponse.GetResponseStream(); //接收回傳訊息
16
17 Encoding
encode = System.Text.Encoding.GetEncoding("utf-8"); //設定編碼方式
18
19 //用 StreamReader 讀出回傳訊息
20 StreamReader
readStream = new StreamReader(msgstream, encode);
21
22 string
Syslog = "";
23
Syslog = readStream.ReadToEnd();
24
readStream.Close();
25
}

26 catch
(Exception e3)
27
{
28
Response.Write(e3.ToString());
29
}
30
}

爬了一下文之後,找到好幾種解決的方法,
例如將頁面設定為可匿名存取(可以過,但是一般不建議這樣的方式),
或是在 web.config 中加入:(試過還是不行)

1 <identity impersonate=”true”/>

 

 

最後找到的解法是在 GetResponse() 之前加上這一段:

1 myHttpWebRequest.Credentials = CredentialCache.DefaultCredentials;

 

這樣就可以用當時使用者當時登錄的身分進行認證,
另外,DefaultCredentials 只記錄了程式運作時的一些認證訊息,
而不會紀錄使用者的帳號和密碼,所以對安全性來說有一定幫助,
如果硬要檢視詳細的內容,也只會得到一堆空字串,
這或許也是微軟的保護機制吧!?



如果你要指定一個身分去登錄網站的話,可以這樣做:

1 myHttpWebRequest.PreAuthenticate = true;
2 NetworkCredential netCreden=new NetworkCredential("userName", "userPW"); //登入的帳號密碼
3 myHttpWebRequest.Credentials = netCreden.GetCredential(new Uri(URL), "authType"); //要瀏覽的網址以及認證類型
4

 

當然,如果你的認證方式是預設值且網址URL跟最前面相同的話,
最後一行可以簡寫為

1 myHttpWebRequest.Credentials=netCreden;