zoukankan      html  css  js  c++  java
  • CEF3开发者系列之Cookies管理和共享<转>

    原帖地址:https://www.cnblogs.com/guolixiucai/p/6994559.html

    涉及网页登录相关的技术,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, CefRefPtrCefCompletionCallback > callback ) 
              Creates a new cookie manager.
     virtual bool DeleteCookies( const CefString& url, const CefString& cookie_name, CefRefPtrCefDeleteCookiesCallback > callback )= 0 
              Delete all cookies that match the specified parameters.
     virtual bool FlushStoreCefRefPtrCefCompletionCallback > callback )= 0 
              Flush the backing store (if any) to disk.
     static CefRefPtr< CefCookieManager > GetGlobalManagerCefRefPtrCefCompletionCallback > callback ) 
              Returns the global cookie manager.
     virtual bool SetCookie( const CefString& url, const CefCookie& cookie, CefRefPtrCefSetCookieCallback > 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, CefRefPtrCefCompletionCallback > callback )= 0 
              Sets the directory path that will be used for storing cookie data.
     virtual void SetSupportedSchemes( const std::vector< CefString >& schemes, CefRefPtrCefCompletionCallback > callback )= 0 
              Set the schemes supported by this manager.
     virtual bool VisitAllCookiesCefRefPtrCefCookieVisitor > visitor )= 0 
              Visit all cookies on the IO thread.
     virtual bool VisitUrlCookies( const CefString& url, bool includeHttpOnly, CefRefPtrCefCookieVisitor > 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。

    实例

    void ClientAppBrowser::SetCookies( std::wstring domain, std::wstring key, std::wstring svalue )
    {
        /*
        向指定的域名写Cookie   http://www.cnblogs.com/guolixiucai/
        */  
        CefRefPtr<CefCookieManager> manager = CefCookieManager::GetGlobalManager(NULL);
        DCHECK(manager.get());
        CefRefPtr<CefSetCookieCallback> callback = NULL;
        CefCookie cookie;
        CefString(&cookie.name).FromWString(key.c_str());
        CefString(&cookie.value).FromWString(svalue.c_str());
        CefString(&cookie.domain).FromWString(domain.c_str());
        CefString(&cookie.path).FromASCII("/");
        cookie.has_expires = true;
           //以下过期时间随意设置的。
        cookie.expires.year = 2200;
        cookie.expires.month = 4;  
        cookie.expires.day_of_week = 5;  
        cookie.expires.day_of_month = 11;  
        std::wstring httpDomain = L"http://www";  
        httpDomain.append(domain);
        base::IgnoreResult(&CefCookieManager::SetCookie);
        CefPostTask(TID_IO, NewCefRunnableMethod(manager.get(), &CefCookieManager::SetCookie,
               CefString(httpDomain.c_str()), cookie, callback));
    }

    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。

    原帖地址:https://www.cnblogs.com/guolixiucai/p/6994559.html

  • 相关阅读:
    java -inally转
    11.Android-Xml读写
    10.Android-SharedPreferences使用
    9.Android-读写SD卡案例
    8.Android-简单的登录案例编写
    7.Android-压力测试、单元测试、日志猫使用
    python 向mysql中存储图片以及读取图片
    Android Studio 4.0+ 中新的 UI 层次结构调试工具
    微信小程序又放大招,代码包的大小限制由1M扩到2M
    硅谷禁书全集(5册)- 带完整封面目录,全面优化版
  • 原文地址:https://www.cnblogs.com/wainiwann/p/10953683.html
Copyright © 2011-2022 走看看