涉及網頁登錄相關的技術,Cookies肯定是忽略不了的。由於項目的需要,要做一個雙核的產品。雙核間切換會涉及到登錄狀態的問題,共享Cookies是一個很好的方案。既然涉及到共享cookies,那么讀取完整的cookies和設置cookies就是問題的關鍵。由於應用本身只需要加載自家的平台,不用考慮共享所有網站cookie的問題,所以需要獲取的和設置的cookies相對比較簡單。
IE瀏覽器內核Cookies的獲取和設置相對難一點,但是好在網絡上資料比較多。Chromium內核基於CEF3框架,獲取和設置cookies有現成的接口。在這里主要總結的是IE瀏覽器內核Cookies的獲取和基於CEF3框架的Cookies設置以及獲取。
IE瀏覽器內核的Cookies獲取的三種方式:
1、通過讀取存在硬盤上的cookies文件,來獲取指定網站的cookies;
2、通過用MSHTMLI的Dispatch接口IWebBrowser2和IHTMLDocument2來獲取cookies;
3、通過API函數InternetGetCookieEx和InternetGetCookie來獲取cookies;
4、通過Hook WINNET 相關的函數獲取Cookies,HOOK住發送和接收包API(send、recv、WSASend、WSARecv等)。然后分析HTTP header。此處不講。
第一種方式,實現起來比較笨重,一般作為后邊兩種方式的補充。實現代碼如下:
std::wstring GetCookiesUnderIE7( wstring strDomain ) { wstring strCookiePath; TCHAR dataPath[MAX_PATH]; ::SHGetSpecialFolderPath(NULL, dataPath, CSIDL_COOKIES, FALSE); strCookiePath = dataPath; TCHAR szUserName[32]; DWORD cbUserName; cbUserName = 32; GetUserName(szUserName, &cbUserName); WIN32_FIND_DATA FindData; HANDLE hfile; DWORD errorcode = 0; hfile = FindFirstFile((strCookiePath + L"\\*.txt").c_str() ,&FindData); wstring wsUserName = szUserName; wstring wsCookieName = wsUserName + L"@xx"; //xx為網站域名,如百度的baidu wstring wsFileName; wstring wsCookies = L""; hile(hfile!= INVALID_HANDLE_VALUE && errorcode != ERROR_NO_MORE_FILES) { wsFileName = FindData.cFileName; //判斷是否存在【user】@xx為名稱的cookie腳本,提高過濾效率 if (wsFileName.find(wsCookieName.c_str()) != wstring::npos) { wsFileName = strCookiePath + L"\\"+ FindData.cFileName; //http://www.cnblogs.com/guolixiucai/; std::ifstream ifsCookie(wsFileName.c_str(), std::ios::in | std::ios::binary); if (ifsCookie) { std::string contents; ifsCookie.seekg(0, std::ios::end); contents.resize(ifsCookie.tellg()); ifsCookie.seekg(0, std::ios::beg); ifsCookie.read(&contents[0], contents.size()); if(contents.find("ispass_37wan_com") != string::npos) { wsCookies = s2ws(contents); ifsCookie.close(); break; } ifsCookie.close(); } } BOOL flag = TRUE; BOOL isNextFile = FindNextFile(hfile,&FindData);//判斷該目錄下是否還有文件 if(flag == TRUE && isNextFile == TRUE)//如果還有文件,則調用SetLastError,設為NO_ERROR,這樣才能繼續遍歷后面的文件 SetLastError(NO_ERROR); else errorcode=GetLastError(); } return wsCookies; }
第二種方式獲取的Cookies並不完全,只能獲取到部分Cookies
IHTMLDocument2::get_cookie()
IHTMLDocument2::put_cookie()
用MSHTML對Document對象的Cookie屬性進行操作。也可以實現類似InternetGetCookie和InternetSetCookie的效果。實際上它等同於用javascript來讀寫文檔中的Cookie。它同樣可以獲取、添加、覆蓋、修改、刪除持久Cookie和會話級Cookie,但不可讀寫HTTPOnly的Cookie。這種方式同樣也要十分注意設置的Cookie的每個屬性都要和目標Cookie一一對應才能正確操作Cookie。特別是Path和Domain屬性,否則會導致添加了一個同名Cookie而不能覆蓋或清除目標Cookie的結果。
代碼如下:
std::wstring BrowserView::GetCookies() { CComPtr<IWebBrowser2> webBrowser; HRESULT hr = QueryControl(IID_IWebBrowser2, reinterpret_cast<void**>(&webBrowser)); if (FAILED(hr) || (!webBrowser)) return L""; CComPtr<IDispatch> disp; hr = webBrowser->get_Document(&disp); if (FAILED(hr) || (!disp)) return L""; CComPtr<IHTMLDocument2> document; hr = disp->QueryInterface(IID_IHTMLDocument2, reinterpret_cast<void**>(&document)); if (FAILED(hr) || (!document)) return L""; BSTR bstrCookie; hr = document->get_cookie(&bstrCookie); BSTR bstrDomain; document->get_domain(&bstrDomain); if (FAILED(hr)) { return L""; } wstring strCookie(bstrCookie); char* lpszText2 = _com_util::ConvertBSTRToString(bstrCookie); SysFreeString(bstrCookie); return strCookie; }
第三種方式:
InternetGetCookie
InternetSetCookie
InternetGetCookieEx
InternetSetCookieEx
這些API屬於Wininet,可以獲取、添加、覆蓋、修改、刪除持久Cookie和會話級Cookie(會話級Cookie需要在同進程中操作)。加Ex后綴的API可以對HTTPOnly的Cookie進行操作。但這組API對IE7及以下的IE瀏覽器內核無效,取不到Cookies,這時候就需要使用第一種方法補充。當然也可以使用其他比較復雜的方法,例如Hook WINNET 相關的函數。
std::wstring GetCookies( wstring strDomain )
{
LPDWORD lpdwSize = new DWORD;
wchar_t strCookie[2048] = {0};
int size = 0;
//InternetGetCookie 取不到httponly的cookie,留在這里僅供后來者參考
// InternetGetCookie(strDomain.c_str(), NULL, NULL, lpdwSize);
// InternetGetCookie(strDomain.c_str(), NULL, strCookie, lpdwSize/*, INTERNET_COOKIE_HTTPONLY, NULL*/);
//InternetGetCookieEx 在IE7和IE6里也是取不全cookie。
InternetGetCookieEx(strDomain.c_str(), NULL, strCookie, lpdwSize, 0x2000, NULL);
wstring wsCookies = strCookie;
return wsCookies;
}
CEF3中Cookies的管理。
如果不想聽我啰嗦,移步http://magpcss.org/ceforum/apidocs3/projects/(default)/CefCookieManager.html
下邊的內容基本上是翻譯這個文檔,在加上兩個實例。
CEF3中,CefCookieManager這個類就是用來管理cookies的。在頭文件cef_cookies中,在cef_cookies_capi.h里,有詳細的注釋,和上邊鏈接里的文檔說明一樣。
Cookies的管理無外乎Cookies的設置、獲取、刪除、查找,外加一個存儲位置的設置。
class CefCookieManager
extends CefBase
Class used for managing cookies. The methods of this class may be called on any thread unless otherwise indicated.
該類用來管理cookies。除非另有說明,該類的方法可以在任何線程中調用
Method Summary | |
static CefRefPtr< CefCookieManager > |
CreateManager( const CefString& path, bool persist_session_cookies, CefRefPtr< CefCompletionCallback > callback ) Creates a new cookie manager. |
virtual bool |
DeleteCookies( const CefString& url, const CefString& cookie_name, CefRefPtr< CefDeleteCookiesCallback > callback )= 0 Delete all cookies that match the specified parameters. |
virtual bool |
FlushStore( CefRefPtr< CefCompletionCallback > callback )= 0 Flush the backing store (if any) to disk. |
static CefRefPtr< CefCookieManager > |
GetGlobalManager( CefRefPtr< CefCompletionCallback > callback ) Returns the global cookie manager. |
virtual bool |
SetCookie( const CefString& url, const CefCookie& cookie, CefRefPtr< CefSetCookieCallback > callback )= 0 Sets a cookie given a valid URL and explicit user-provided cookie attributes. |
virtual bool |
SetStoragePath( const CefString& path, bool persist_session_cookies, CefRefPtr< CefCompletionCallback > callback )= 0 Sets the directory path that will be used for storing cookie data. |
virtual void |
SetSupportedSchemes( const std::vector< CefString >& schemes, CefRefPtr< CefCompletionCallback > callback )= 0 Set the schemes supported by this manager. |
virtual bool |
VisitAllCookies( CefRefPtr< CefCookieVisitor > visitor )= 0 Visit all cookies on the IO thread. |
virtual bool |
VisitUrlCookies( const CefString& url, bool includeHttpOnly, CefRefPtr< CefCookieVisitor > visitor )= 0 Visit a subset of cookies on the IO thread. |
Method Detail
CreateManager
public static CefRefPtr< CefCookieManager > CreateManager( const CefString& path, bool persist_session_cookies, CefRefPtr< CefCompletionCallback > callback );
創建一個新的cookie管理器,當|path|沒有被設置時,數據存儲在內存中。否則,數據保存在指定的|path|(如果在cef初始化時,指定了緩存路徑,那么cookies會保存在此路徑)。持久化會話cookies(沒有有效期或有效區域),需要設置|persist_session_cookies| 為true。會話cookies一般是暫時性的並且大多數瀏覽器並不支持。如果|callback| 為 non-NULL ,那么在管理器存儲空間初始化后,會在IO線程中異步執行。
DeleteCookies
public virtual bool DeleteCookies( const CefString& url, const CefString& cookie_name, CefRefPtr< CefDeleteCookiesCallback > callback )= 0;
刪除與指定參數的相匹配的所有cookies。如果 |url| 和 |cookie_name| 都被指定,那么 host和domian與這兩個參數匹配的都被刪除。如果 |url|為空,那么所有的hosts 和domains的cookies都沒清空。如果|callback| 為 non-NULL ,那么在cookies被刪除后,該回調函數會在IO線程中異步執行。如果不存在指定的url或者cookies不能被訪問,那么返回falsh。cookies可以通過交替使用Visit*Cookies()方法被刪除。
//向指定的域名刪除cookies
void ClientAppBrowser::DeleteCookies( std::wstring domain, std::wstring cookiename ) { CefRefPtr<CefCookieManager> manager = CefCookieManager::GetGlobalManager(NULL); DCHECK(manager.get()); CefRefPtr<CefDeleteCookiesCallback> callback = NULL; std::wstring httpDomain = domain; CefString cookie_name; cookie_name.FromWString(cookiename .c_str()); base::IgnoreResult(&CefCookieManager::DeleteCookies); CefPostTask(TID_IO, NewCefRunnableMethod(manager.get(), &CefCookieManager::DeleteCookies, CefString(httpDomain.c_str()), cookie_name, callback)); }
FlushStore
public virtual bool FlushStore( CefRefPtr< CefCompletionCallback > callback )= 0;
將備份存儲(如果存在)緩存到硬盤,如果|callback| 為 non-NULL ,在緩存完后,該回調函數會在IO線程中異步執行。cookies不能被訪問則返回falsh。
GetGlobalManager
public static CefRefPtr< CefCookieManager > GetGlobalManager( CefRefPtr< CefCompletionCallback > callback );
返回全局cookie管理器,默認存儲路徑是 CefSettings.cache_path或者其他指定路徑,否則存儲在內存。如果|callback| 為 non-NULL ,在管理器存儲空間初始化后,該回調函數會在IO線程中異步執行。該方法與調用CefRequestContext::GetGlobalContext()->GetDefaultCookieManager()相當
SetCookie
public virtual bool SetCookie( const CefString& url, const CefCookie& cookie, CefRefPtr< CefSetCookieCallback > callback )= 0;
設置一個指定了url和由用戶設置了屬性的cookie。該方法需要每個屬性有符合良好的格式。檢查非法字符(例如“;”存在cookie屬性值中),存在非法字符會導致cookies設置失敗。如果|callback| 為 non-NULL ,那么在cookies被設置后,該回調函數會在IO線程中異步執行。指定的url不存在或者cookies不能被訪問,返回falsh。
實例
1 void ClientAppBrowser::SetCookies( std::wstring domain, std::wstring key, std::wstring svalue ) 2 { 3 /* 4 向指定的域名寫Cookie http://www.cnblogs.com/guolixiucai/ 5 */ 6 CefRefPtr<CefCookieManager> manager = CefCookieManager::GetGlobalManager(NULL); 7 DCHECK(manager.get()); 8 CefRefPtr<CefSetCookieCallback> callback = NULL; 9 CefCookie cookie; 10 CefString(&cookie.name).FromWString(key.c_str()); 11 CefString(&cookie.value).FromWString(svalue.c_str()); 12 CefString(&cookie.domain).FromWString(domain.c_str()); 13 CefString(&cookie.path).FromASCII("/"); 14 cookie.has_expires = true; 15 //以下過期時間隨意設置的。 16 cookie.expires.year = 2200; 17 cookie.expires.month = 4; 18 cookie.expires.day_of_week = 5; 19 cookie.expires.day_of_month = 11; 20 std::wstring httpDomain = L"http://www"; 21 httpDomain.append(domain); 22 base::IgnoreResult(&CefCookieManager::SetCookie); 23 CefPostTask(TID_IO, NewCefRunnableMethod(manager.get(), &CefCookieManager::SetCookie, 24 CefString(httpDomain.c_str()), cookie, callback)); 25 }
SetStoragePath
public virtual bool SetStoragePath( const CefString& path, bool persist_session_cookies, CefRefPtr< CefCompletionCallback > callback )= 0;
設置存儲cookies數據的路徑。如果|path|為空,則存儲在內存中。持久化會話cookies(沒有有效期或有效區域),需要設置|persist_session_cookies| 為true。會話cookies一般是暫時性的並且大多數瀏覽器並不支持。如果|callback| 為 non-NULL ,那么在管理器存儲空間初始化后,會在IO線程中異步執行。
SetSupportedSchemes
public virtual void SetSupportedSchemes( const std::vector< CefString >& schemes, CefRefPtr< CefCompletionCallback > callback )= 0;
設置管理器支持的協議。支持常見的協議("http", "https", "ws" and "wss")。如果|callback| 為 non-NULL ,在協議更改生效后,該回調函數會在IO線程中異步執行。該方法必須在所有cookies被訪問前調用
VisitAllCookies
public virtual bool VisitAllCookies( CefRefPtr< CefCookieVisitor > visitor )= 0;
訪問IO線程中所有的Cookies。返回的cookies按照最長路徑排序,然后再按照最長創建時間排序。如果cookies被占用則返回false。
VisitUrlCookies
public virtual bool VisitUrlCookies( const CefString& url, bool includeHttpOnly, CefRefPtr< CefCookieVisitor > visitor )= 0;
訪問IO線程上的一組Cookie。返回結果可按照url、host、domain和路徑過濾。如果|includeHttpOnly|為ture,那么結果中還會包含HTTP-only cookies。返回的cookies按照最長路徑排序,然后再按照最長創建時間排序。如果cookies被占用則返回false。