Java サーバ側で ブラウザから送付された Cookie の expire、maxage は取得できない


Java で、Cookie の値を取得方法を調べていたのですが、キー、Value は取得できるとして、有効期限の値が取得できないのができるのかよくわからず、実際にプログラムを書いて試してみました。 結果を以下に記載します。


前提

以下の環境で実施しています。

  • OS

    % sw_vers
    ProductName:    Mac OS X
    ProductVersion: 10.13.4
    BuildVersion:   17E202
    

  • Java

    % java -version
    java version "1.8.0_45"
    Java(TM) SE Runtime Environment (build 1.8.0_45-b14)
    Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)
    

  • Dropwizard の version

    <dependency>
        <groupId>io.dropwizard</groupId>
        <artifactId>dropwizard-core</artifactId>
        <version>1.1.2</version>
    </dependency>
    

  • 使用ブラウザ

    Chrome
    バージョン: 66.0.3359.181(Official Build) (64 ビット)
    


結論

結果として、サーバ側では、expires、max-age の値は取得できませんでした。
サーバ側で、

String rawCookie = httpRequest.getHeader("Cookie");
として Header から取得しても、キー値自体取得できず、Chrome から、expires、max-age が送付されていないようです。


検証用プログラム

以下 Resource クラスと、Application クラスを作成して、localhost でアプリケーションを起動し、Cookie を設定して、Resource クラスの戻り値で取得状況を確認しました。
* CookieResource

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;

@Path("cookies")
@Produces(MediaType.APPLICATION_JSON)
public class CookieResource {
    @GET
    public String toString(@Context HttpServletRequest httpRequest) {
        javax.servlet.http.Cookie[] cookies = httpRequest.getCookies();
        StringBuilder sb = new StringBuilder();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                sb.append(encodeCookie(cookie));
                sb.append("\n");
                sb.append("Cookie#toString() " + cookie.toString());
                sb.append("\n");
                sb.append("---------------");
                sb.append("\n");
            }
        }
        String rawCookie = httpRequest.getHeader("Cookie");
        sb.append("rawCookie#" + rawCookie);
        return sb.toString();
    }

    /**
     * Encode a cookie as per RFC 2109.  The resulting string can be used
     * as the value for a <code>Set-Cookie</code> header.
     *
     * @param cookie The cookie to encode.
     * @return A string following RFC 2109.
     */
    public static String encodeCookie(Cookie cookie) {

        StringBuffer buf = new StringBuffer(cookie.getName());
        buf.append("=");
        buf.append(cookie.getValue());

        if (cookie.getComment() != null) {
            buf.append("; Comment=\"");
            buf.append(cookie.getComment());
            buf.append("\"");
        }

        if (cookie.getDomain() != null) {
            buf.append("; Domain=\"");
            buf.append(cookie.getDomain());
            buf.append("\"");
        }

        long age = cookie.getMaxAge();
        if (cookie.getMaxAge() >= 0) {
            buf.append("; Max-Age=\"");
            buf.append(cookie.getMaxAge());
            buf.append("\"");
        }

        if (cookie.getPath() != null) {
            buf.append("; Path=\"");
            buf.append(cookie.getPath());
            buf.append("\"");
        }

        if (cookie.getSecure()) {
            buf.append("; Secure");
        }

        if (cookie.getVersion() > 0) {
            buf.append("; Version=\"");
            buf.append(cookie.getVersion());
            buf.append("\"");
        }

        return (buf.toString());
    }
}

  • WebApplication.java
    import io.dropwizard.Application;
    import io.dropwizard.Configuration;
    import io.dropwizard.setup.Environment;
    import xyz.monotalk.festivals4partypeople.web.dropwizard.resources.CookieResource;
    
    public class WebApplication extends Application<Configuration> {
    
        public static void main(String[] args) throws Exception {
            new WebApplication().run(args);
        }
    
        @Override
        public String getName() {
            return "WebApplication";
        }
    
        @Override
        public void run(Configuration t, Environment e) throws Exception {
            // Resourceを追加
            e.jersey().register(new CookieResource());
        }
    }
    

検証結果

JavaScript で cookie を設定し、Web サービスにアクセスして、Cookie の取得状況を確認します。

キー値、Value のみを設定する

  • JavaScript

    document.cookie = 'data=test';
    

  • Cookie の取得状況

    data=test
    Cookie#toString() javax.servlet.http.Cookie@7bb02735
    ---------------
    rawCookie#data=test
    
    max-age のない場合は、Cookie は session Cookie になります。

キー値、Value、max-age を設定する

  • JavaScript

    document.cookie = 'data=test; max-age=1000000';
    

  • Cookie の取得状況

    data=test
    Cookie#toString() javax.servlet.http.Cookie@13b5d6bc
    ---------------
    rawCookie#data=test
    

キー値、Value、max-age を設定する。 max-age は 1

  • Javascript
    期限切れの場合の動作の検証です。

    document.cookie = 'data=test; max-age=1';
    

  • Cookie の取得状況
    キー値、Value が取得できなくなりました。

    rawCookie#null
    

キー値、Value、expires を設定する。

  • Javascript

    document.cookie = "data=test; expires=Thu, 01 Feb 2019 00:00:00 GMT";
    

  • Cookie の取得状況

    data=test
    Cookie#toString() javax.servlet.http.Cookie@4e287944
    ---------------
    rawCookie#data=test
    

キー値、Value、expires を設定する。 expires は過去日付

  • Javascript

    document.cookie = "data=test; expires=Thu, 01 Feb 2017 00:00:00 GMT";
    

  • Cookie の取得状況

    rawCookie#null
    

矛盾する、max-age、 expires を設定する

  • Javascript

    document.cookie = "data=test; max-age=10000000, expires=Thu, 01 Feb 2019 00:00:00 GMT";
    

  • Cookie の取得状況
    取得できたので、どちらかが効いているようです。

    data=test
    Cookie#toString() javax.servlet.http.Cookie@353f71e4
    ---------------
    rawCookie#data=test
    

max-age、 expires の優先順位の確認

  • Javascript max-age を短く設定

    document.cookie = "data=test; max-age=3, expires=Thu, 01 Feb 2019 00:00:00 GMT";
    

  • Cookie の取得状況

    rawCookie#null
    

  • Javascript expires を短く設定

    document.cookie = "data=test; max-age=1000000, expires=Wed, 06 Jun 2018 01:08:50 GMT";
    

  • Cookie の取得状況
    max-age のほうが優先順位が高いです。

    data=test
    Cookie#toString() javax.servlet.http.Cookie@33bbf21b
    ---------------
    rawCookie#data=test
    

Expires vs max-age, which one takes priority if both are declared in a HTTP response? - Stack Overflow を見る限り、RFC で優先順位が定められているようです。


参考

以下、Cookie 関連を調べていて参考になった記事になります。
* RFC 6265 — HTTP State Management Mechanism (日本語訳)
* M-Fr Net Miscellany Cookie
* Apache TomcatとHTTPクッキーにまつわる騒動 - @int128
* Why Max-Age cookie attribute is not used instead of a hairy Expires attribute · Issue #421 · expressjs/session · GitHub
* java - Expires string in cookie header - Stack Overflow
* Cookieのexpireの扱いがブラウザによってちょっと異なる件 - mikanmarusanのブログ
* Cookie の仕様とセキュリティ
* 解決編!!Google Chrome Cookie expires(有効期限)バグ – 真夜中に奔る
* document.cookie - Web API インターフェイス | MDN
* クッキーの使用方法 | JavaScript プログラミング解説 * Learn how HTTP Cookies work

javax.servlet.http.Cookie の JavaDoc を見る限りは、特にそれらしき記載はないので、勘違いしていました。
Cookie#getMaxAge() は、サーバ側で設定した Cookie の存続期間 を取得するものかと思いました。
以上です。

コメント