C#利用wininet獲取網頁Cookie
WinInet
WinInet(“Windows Internet”)API幫助程式設計師使用三個常見的Internet協議,這三個協議是用於World Wide Web全球資訊網的超文字傳輸協議(HTTP:Hypertext Transfer Protocol)、檔案傳輸協議(FTP:File Transfer Protocol)和另一個稱為Gopher的檔案傳輸協議。
WinInet函式的語法與常用的Win32 API函式的語法類似,這使得使用這些協議就像使用本地硬碟上的檔案一樣容易。
1、WinInet 是一個網路程式設計介面,包含了 Internet 底層協議 HTTP,FTP。
2、藉助 WinInet 介面,可不必去了解 Winsock、TCP/IP 和特定 Internet 協議
的細節就可以編寫出高水平的 Internet 客戶端程式。
3、WinInet 為 HTTP、FTP 提供了統一的函式集,也就是 Win32 API 介面。
4、WinInet 簡化了 HTTP、FTP 協議的程式設計,可輕鬆地將 Internet 整合到應用程式中。
Cookie
cookie的概念與作用請檢視該連結:
https://www.cnblogs.com/andy-zhou/p/5360107.html
只記錄一下cookie與session的區別:
Session是另一種記錄客戶狀態的機制,不同的是Cookie儲存在客戶端瀏覽器中,而Session儲存在伺服器上。客戶端瀏覽器訪問伺服器的時候,伺服器把客戶端資訊以某種形式記錄在伺服器上。這就是Session。客戶端瀏覽器再次訪問時只需要從該Session中查詢該客戶的狀態就可以了。
如果說Cookie機制是通過檢查客戶身上的“通行證”來確定客戶身份的話,那麼Session機制就是通過檢查伺服器上的“客戶明細表”來確認客戶身份。Session相當於程式在伺服器上建立的一份客戶檔案,客戶來訪的時候只需要查詢客戶檔案表就可以了。
實現
一、開啟網頁
窗體新增webBrowser元件
private void btn_Open_Click(object sender, EventArgs e)
{
this.webBrowser1.Navigate(this.txt_url.Text);//開啟網頁
}
二、定義flag和呼叫API
從託管程式碼中訪問非託管DLL函式之前,需要知道該函式的名稱以及該DLL的名稱,然後為DLL的非託管函式編寫託管定義。
它將用到static和extern修飾符,此型別的公共靜態成員對於多執行緒操作是安全的。DllImport屬性提供非託管DLL函式的呼叫資訊。
internal sealed class NativeMethods
{
#region enums
public enum ErrorFlags
{
ERROR_INSUFFICIENT_BUFFER = 122,
ERROR_INVALID_PARAMETER = 87,
ERROR_NO_MORE_ITEMS = 259
}
public enum InternetFlags
{
INTERNET_COOKIE_HTTPONLY = 8192, //Requires IE 8 or higher
INTERNET_COOKIE_THIRD_PARTY = 131072,
INTERNET_FLAG_RESTRICTED_ZONE = 16
}
#endregion
#region DLL Imports
[SuppressUnmanagedCodeSecurity, SecurityCritical, DllImport("wininet.dll", EntryPoint = "InternetGetCookieExW", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
internal static extern bool InternetGetCookieEx([In] string Url, [In] string cookieName, [Out] StringBuilder cookieData, [In, Out] ref uint pchCookieData, uint flags, IntPtr reserved);
#endregion
}
三、獲取cookie
獲取cookie的方法有很多,其中firefox上有很多外掛就是獲取cookie的,其中chrome上有一個EditThisCookie的外掛挺好用,但是整個cookie沒有返回給你,只是逐條顯示。而利用在瀏覽器位址列輸入javascript:alert (document. cookie)的方法取不到HttpOnly的cookie,所以使用wininetAPI能夠取得完整cookie並且可以根據你想要的格式返回給你。
構造獲取cookie的類,首先把url轉為string,獲取訪問url的許可權,然後利用wininet下的InternetGetCookieEx獲取cookie,返回為string格式。
public class FullWebBrowserCookie
{
[SecurityCritical]
public static string GetCookieInternal(Uri uri, bool throwIfNoCookie)
{
uint pchCookieData = 0;
string url = UriToString(uri);
uint flag = (uint)NativeMethods.InternetFlags.INTERNET_COOKIE_HTTPONLY;
//獲取 string builder的大小
if (NativeMethods.InternetGetCookieEx(url, null, null, ref pchCookieData, flag, IntPtr.Zero))
{
pchCookieData++;
StringBuilder cookieData = new StringBuilder((int)pchCookieData);
//讀取cookie
if (NativeMethods.InternetGetCookieEx(url, null, cookieData, ref pchCookieData, flag, IntPtr.Zero))
{
DemandWebPermission(uri);
return cookieData.ToString();
}
}
//返回由上一個非託管函式返回的錯誤程式碼呼叫的dll檔案函式
int lastErrorCode = Marshal.GetLastWin32Error();
if (throwIfNoCookie || (lastErrorCode != (int)NativeMethods.ErrorFlags.ERROR_NO_MORE_ITEMS))
{
throw new Win32Exception(lastErrorCode);
}
return null;
}
private static void DemandWebPermission(Uri uri)
{
string uriString = UriToString(uri);
if (uri.IsFile)
{
string localPath = uri.LocalPath;
new FileIOPermission(FileIOPermissionAccess.Read, localPath).Demand();
//如果未對呼叫堆疊中處於較高位置的所有呼叫方授予當前例項所指定的許可權,則在執行時強制SecurityException
}
else
{
new WebPermission(NetworkAccess.Connect, uriString).Demand();
}
}
//URI轉string
private static string UriToString(Uri uri)
{
if (uri == null)
{
throw new ArgumentNullException("uri");
}
UriComponents components = (uri.IsAbsoluteUri ? UriComponents.AbsoluteUri : UriComponents.SerializationInfoString);//獲取絕對url
return new StringBuilder(uri.GetComponents(components, UriFormat.SafeUnescaped), 2083).ToString();
}
}
四、整理cookie
返回cookie的string型別
private static string GetCookieString(string url)
{
return FullWebBrowserCookie.GetCookieInternal(new Uri(url), true);
}
按照格式以“;”分行,並以“=”來檢視cookie的個數。
public CookieContainer getCookie(string cookieStr)
{
CookieContainer myCookieContainer = new CookieContainer();
string CurHost = this.webBrowser1.Url.Host;
//string cookieStr = webBrowser1.Document.Cookie;
string[] cookstr = cookieStr.Split(';');
string flag = "";
foreach (string str in cookstr)
{
if (str.IndexOf("_saltkey") != -1)
{
string[] cookieNameValue = str.Split('=');
flag = cookieNameValue[0].Replace("_saltkey", "").Trim();
}
}
//
//MessageBox.Show(flag);
//flag = "";
myCookieContainer.PerDomainCapacity = 40;
foreach (string str in cookstr)
{
try
{
string[] cookieNameValue = str.Split('=');
string strvalue = cookieNameValue[1].Trim().ToString().Replace(",", "%2C");
strvalue = str.Replace(cookieNameValue[0] + "=", "");
strvalue = strvalue.Trim().ToString().Replace(",", "%2C");
Cookie ck = new Cookie(cookieNameValue[0].Trim().ToString(), strvalue);
ck.Domain = CurHost;
myCookieContainer.Add(ck);
}
catch (Exception ex) { MessageBox.Show(ex.Message + ">>\r\n" + cookieStr); }
}
return myCookieContainer;
}
和this.webBrowser1.Document.Cookie進行對比
private void btn_Set_Click(object sender, EventArgs e)
{
var realCookie = getCookie(GetCookieString(this.txt_url.Text));
var bwCookie = getCookie(this.webBrowser1.Document.Cookie);
textBox1.Text = getCookie(this.webBrowser1.Document.Cookie).Count.ToString() + "--" + getCookie(GetCookieString(this.txt_url.Text)).Count.ToString() + "\r\n" + this.webBrowser1.Document.Cookie + "\r\n new:\r\n" + GetCookieString(this.txt_url.Text);
textBox2.Text = bwCookie.Count.ToString() + ":\r\n" + bwCookie.GetCookieHeader(new Uri(this.txt_url.Text)).Replace(";", ";\r\n") + "\r\n 真實cookies(" + realCookie.Count.ToString() + "):\r\n" + realCookie.GetCookieHeader(new Uri(this.txt_url.Text)).Replace(";", ";\r\n");
}
五、測試
在該窗體登入163郵箱並儲存賬號密碼,獲取該郵箱cookie。
如下圖,發現使用wininet獲取了31項cookie,而使用`Document.Cookie`獲取了28項,所以wininet可以獲取到httponly限定之外的cookie。