CEF3開發者系列之Cookies管理和共享

涉及網頁登陸相關的技術,Cookies確定是忽略不了的。因爲項目的須要,要作一個雙核的產品。雙核間切換會涉及到登陸狀態的問題,共享Cookies是一個很好的方案。既然涉及到共享cookies,那麼讀取完整的cookies和設置cookies就是問題的關鍵。因爲應用自己只須要加載自家的平臺,不用考慮共享全部網站cookie的問題,因此須要獲取的和設置的cookies相對比較簡單。
IE瀏覽器內核Cookies的獲取和設置相對難一點,可是好在網絡上資料比較多。Chromium內核基於CEF3框架,獲取和設置cookies有現成的接口。在這裏主要總結的是IE瀏覽器內核Cookies的獲取和基於CEF3框架的Cookies設置以及獲取。

IE瀏覽器內核的Cookies獲取的三種方式:
一、經過讀取存在硬盤上的cookies文件,來獲取指定網站的cookies;
二、經過用MSHTMLI的Dispatch接口IWebBrowser2和IHTMLDocument2來獲取cookies;
三、經過API函數InternetGetCookieEx和InternetGetCookie來獲取cookies;
四、經過Hook WINNET 相關的函數獲取Cookies,HOOK住發送和接收包API(send、recv、WSASend、WSARecv等)。而後分析HTTP header。此處不講。
第一種方式,實現起來比較笨重,通常做爲後邊兩種方式的補充。實現代碼以下:javascript

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的結果。
代碼以下:css

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。除非另有說明,該類的方法能夠在任何線程中調用

html

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線程中異步執行。java


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()方法被刪除。

//向指定的域名刪除cookiesios

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。web


GetGlobalManager
public static CefRefPtr< CefCookieManager > GetGlobalManager( CefRefPtr< CefCompletionCallback > callback );
返回全局cookie管理器,默認存儲路徑是 CefSettings.cache_path或者其餘指定路徑,不然存儲在內存。若是|callback| 爲 non-NULL ,在管理器存儲空間初始化後,該回調函數會在IO線程中異步執行。該方法與調用CefRequestContext::GetGlobalContext()->GetDefaultCookieManager()至關api


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被訪問前調用cookie

VisitAllCookiespublic virtual bool VisitAllCookies( CefRefPtr< CefCookieVisitor > visitor )= 0;訪問IO線程中全部的Cookies。返回的cookies按照最長路徑排序,而後再按照最長建立時間排序。若是cookies被佔用則返回false。VisitUrlCookiespublic 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。

相關文章
相關標籤/搜索