Pablo Fernandez
Pablo Fernandez

Reputation: 287830

Access all the cookies from a JavaFX WebView

I need to access all the cookies from a JavaFX WebView. As far as I can see there's a com.sun.webkit that implements its own CookieManager, Cookie, etc, etc. In that implementation, the cookies are hidden away and there's no way to access them.

Am I correct in thinking there are only two solutions:

Am I missing anything here?

Implementing my own CookieManager is no trivial task. com.sun.webkit's is more than 1500 lines of code.

Using reflection to access the internals is quite horrible. So far, I managed to dump the cookies to a JSON file but look at the code:

 private void saveCookies() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException, ClassNotFoundException, IOException {
    CookieManager cookieManager = (CookieManager) CookieHandler.getDefault();
    Field f = cookieManager.getClass().getDeclaredField("store");
    f.setAccessible(true);
    Object cookieStore = f.get(cookieManager);

    Field bucketsField = Class.forName("com.sun.webkit.network.CookieStore").getDeclaredField("buckets");
    bucketsField.setAccessible(true);
    Map buckets = (Map) bucketsField.get(cookieStore);
    f.setAccessible(true);
    Map<String, Collection> cookiesToSave = new HashMap<>();
    for (Object o : buckets.entrySet()) {
        Map.Entry pair = (Map.Entry) o;
        String domain = (String) pair.getKey();
        Map cookies = (Map) pair.getValue();
        cookiesToSave.put(domain, cookies.values());
    }

    Gson gson = new GsonBuilder().create();
    String json = gson.toJson(cookiesToSave);
    System.out.println(json);

    Files.write(Paths.get("cookies.json"), json.getBytes());
}

Following @James_D comments, I tried making a wrapper for CookieManager, something like this:

package sample;

import com.sun.webkit.network.CookieManager;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.CookieHandler;
import java.net.URI;
import java.util.*;

public class MyCookieManager extends CookieHandler {
    private final CookieManager cookieManager = new CookieManager();
    private final Set<URI> uris = new HashSet<>();

    MyCookieManager() {

    }

    @Override
    public Map<String, List<String>> get(URI uri, Map<String, List<String>> requestHeaders) throws IOException {
        uris.add(uri);
        return cookieManager.get(uri, requestHeaders);
    }

    @Override
    public void put(URI uri, Map<String, List<String>> responseHeaders) throws IOException {
        uris.add(uri);
        cookieManager.put(uri, responseHeaders);
    }

    void save() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        System.out.println("Saving cookies");
        System.out.println(uris);
        for (URI uri : uris) {
            System.out.println(uri);
            System.out.println(cookieManager.get(uri, new HashMap<>()));
        }
    }
}

After accessing Google, when I call save, I get this:

https://ssl.gstatic.com/gb/images/b_8d5afc09.png
{}
https://www.google.co.uk/images/branding/googlelogo/1x/googlelogo_white_background_color_272x92dp.png
{Cookie=[NID=110=TnISuEnlryFjSzvLgWXIuTBRDx_CTBlgPOqFzCPXM88M0xQY_1nSJEG2uyCxk-RFNbdexuen5A_3CZXHevseXhRSKJmRSHis0pUb4kvFCxliWT8RsKvRXoAxBYJhGGd2]}
https://www.google.co.uk/client_204?&atyp=i&biw=500&bih=455&ei=Q72cWa_IJ4KhUc6xpqAH
{Cookie=[NID=110=TnISuEnlryFjSzvLgWXIuTBRDx_CTBlgPOqFzCPXM88M0xQY_1nSJEG2uyCxk-RFNbdexuen5A_3CZXHevseXhRSKJmRSHis0pUb4kvFCxliWT8RsKvRXoAxBYJhGGd2]}
https://clients1.google.co.uk/generate_204
{Cookie=[NID=110=TnISuEnlryFjSzvLgWXIuTBRDx_CTBlgPOqFzCPXM88M0xQY_1nSJEG2uyCxk-RFNbdexuen5A_3CZXHevseXhRSKJmRSHis0pUb4kvFCxliWT8RsKvRXoAxBYJhGGd2]}
https://www.google.co.uk/xjs/_/js/k=xjs.hp.en_US.HWKnNOqE224.O/m=sb_he,d/am=ABg/rt=j/d=1/t=zcms/rs=ACT90oFd7WTU33BO8_uKNZdqfket_iKTeg
{Cookie=[NID=110=TnISuEnlryFjSzvLgWXIuTBRDx_CTBlgPOqFzCPXM88M0xQY_1nSJEG2uyCxk-RFNbdexuen5A_3CZXHevseXhRSKJmRSHis0pUb4kvFCxliWT8RsKvRXoAxBYJhGGd2]}
https://google.com/
{}
https://www.google.co.uk/?gfe_rd=cr&ei=Q72cWb30H9P38Ae7lKagCw
{Cookie=[NID=110=TnISuEnlryFjSzvLgWXIuTBRDx_CTBlgPOqFzCPXM88M0xQY_1nSJEG2uyCxk-RFNbdexuen5A_3CZXHevseXhRSKJmRSHis0pUb4kvFCxliWT8RsKvRXoAxBYJhGGd2]}
https://www.google.co.uk/images/nav_logo229.png
{Cookie=[NID=110=TnISuEnlryFjSzvLgWXIuTBRDx_CTBlgPOqFzCPXM88M0xQY_1nSJEG2uyCxk-RFNbdexuen5A_3CZXHevseXhRSKJmRSHis0pUb4kvFCxliWT8RsKvRXoAxBYJhGGd2]}
https://ssl.gstatic.com/gb/js/sem_e63c08fa6f0a34d4ef28b1bece13b39d.js
{}

As you can see there's a lot of repetition and I don't have access to a lot of the original information of the cookie. In this case, it should be just one cookie (I think) that looks like this:

{
  "google.co.uk": [
    {
      "name": "NID",
      "value": "110\u003dZVaVkKbDxN3aQJYtBTWUQ9G5yc4pGb4TpB4EmTJA9bUB2a27ukBdWh28arRI52lw2Tt9hTtXxLhuWFQl55JaQXUJ3hwQzAFXKI0FB-RCU67MmgvCj9wxgqs9yg7BzBCo",
      "expiryTime": 1519255457000,
      "domain": "google.co.uk",
      "path": "/",
      "creationTime": {
        "baseTime": 1503444256974,
        "subtime": 0
      },
      "lastAccessTime": 1503444257350,
      "persistent": true,
      "hostOnly": false,
      "secureOnly": false,
      "httpOnly": true
    }
  ]
}

Upvotes: 1

Views: 5813

Answers (2)

n247s
n247s

Reputation: 1912

There is no direct way in java as far as I know, but you can communicate with the javascript side of a webPage, meaning you could do something like this: (not tested!)

WebView wv; //assumed to be initialized
WebEngine we = wv.getEngine();
String cookie = (String)we.executeScript("return document.cookie"):

Yes this means you got the usual restrictions to cookie access as you normally have in javascript. But it might be enough in your case.

EDIT:

Apparently its also possible using the java.net library (e.g. java.net.CookieHandler.getDefault()). In this SO post you can find a bit more on how to use it, and here is the CookieManager documentation.

Upvotes: 1

James_D
James_D

Reputation: 209694

(Updated after doing some research...)

Java's default implementation of CookieHandler is java.net.CookieManager, which provides the API and functionality I think you are looking for (supported by the CookieStore and HttpCookie classes). Referring to Setting a cookie using JavaFX's WebEngine/WebView and reading between the lines a little, it seems that java.net.CookieManager supports an obsolete standard (RFC 2965), and because of that, the JavaFX team replaced it with a non-public-API class that (I am assuming) supports the later RFC 6265. The private-API implementation, com.sun.webkit.network.CookieManager is not a subclass of java.net.CookieManager and doesn't provide access to any of the other java.net supporting classes. Note also that using any of the API from that class directly will prevent you updating to Java 9 at any point.

So I think you have two choices. If you can live with a RFC 2965-compliant implementation, all you need to do is to revert to the java.net.CookieManager implementation: just call

CookieManager cookieManager = new CookieManager();
CookieHandler.setDefault(cookieManager);

after instantiating the WebView. Then you can use the standard API to access details of the cookies:

cookieManager.getCookieStore().getCookies().forEach(cookie -> {
    String name = cookie.getName();
    String value = cookie.getValue();
    String domain = cookie.getDomain();
    long maxAge = cookie.getMaxAge(); // seconds
    boolean secure = cookie.getSecure();

    // etc...
});

Note that the HttpCookie class defines a matching algorithm to determine if a particular host is part of a particular domain, so it avoids replicating cookies over multiple URIs belonging to the same domain.

If you need the more recent standard, then it seems you either have to do some fairly dirty hacking on the non-public classes, or you need to implement your own cookie handler (which as you've observed, is not a trivial task).

Upvotes: 3

Related Questions