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。