プログラミング

設計・テスト・レビュー

Cookie削除処理の例で見る、型安全と凝集性を意識したstaticメソッドの使い方

Cookieを削除するコードを題材に、Cookieごとの設定をどこへ置くかを考えます。例はHTTPSで動くアプリを想定し、セッションCookieは JSESSIONID、Pathは /、Domain属性は指定しません。

レビューで見た残念なコード

まず、期限切れのCookieを作るだけなら次の形で動きます。Cookieが増えるにつれて、これと同じクラスに別のCookieの生成や削除が加わる構成です。

import jakarta.servlet.http.Cookie;

public class CookieLogic {
    public Cookie deleteJsessionCookie() {
        Cookie cookie = new Cookie("JSESSIONID", "");
        cookie.setPath("/");
        cookie.setMaxAge(0);
        cookie.setSecure(true);
        cookie.setHttpOnly(true);
        return cookie;
    }
}
CookieLogic logic = new CookieLogic();
response.addCookie(logic.deleteJsessionCookie());
CookieLogicが複数のCookieの生成と削除を持つ構成

何が残念か

Cookieごとに用途と有効期限が違います。全メソッドが同じ Cookie 型を返すと、セッションCookie専用の処理へ別のCookieを渡してもコンパイルで気付けません。

処方箋

設定を変更する理由が異なるCookieは、必要に応じて型や生成処理を分けます。呼び出し側はCookie名を文字列で繰り返し指定せず、用途に対応するファクトリを使います。

修正例

セッション用の型に名前と期限切れの設定をまとめます。ログアウトではサーバー側のセッションも無効化し、期限切れのCookieを応答に追加します。

import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;

public final class JSESSIONID extends Cookie {
    private JSESSIONID() {
        super("JSESSIONID", "");
        setPath("/");
        setSecure(true);
        setHttpOnly(true);
        setMaxAge(0);
    }

    public static JSESSIONID deleteCookie() {
        return new JSESSIONID();
    }

    public static void logout(HttpServletRequest request, HttpServletResponse response) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            session.invalidate();
        }
        response.addCookie(deleteCookie());
    }
}
Cookieごとに型と生成処理を持つ構成

既存のCookieを消すには、発行時の名前・Path・Domainと対応させます。Pathが /app の構成なら /app を使い、Domainを付けて発行する構成なら同じDomainを指定します。Secure を付けた例はHTTPSから返してください。

動作確認では、Cookieの期限と設定、既存セッションの無効化、セッションがないリクエストで新規発行しないことを確認します。既存のセキュリティ機構がログアウトを管理しているアプリでは、そのログアウト設定を使います。

-設計・テスト・レビュー
-