You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Phoenix框架:Java解码Session Cookie乱码的原因及解决方法

Phoenix Session Cookie解码:Java乱码问题及解决方法

问题背景

我尝试用两种方式解码Phoenix Session Cookie:

IEx(Elixir交互shell)示例

iex(1)> set_cookie = "SFMyNTY.g3QAAAABbQAAAAtfY3NyZl90b2tlbm0AAAAYZFRuNUtQMkJ5YWtKT1JnWUtCeXhmNmdP.l0T3G-i8I5dMwz7lEZnQAeK_WeqEZTxcDeyNY2poz_M"
"SFMyNTY.g3QAAAABbQAAAAtfY3NyZl90b2tlbm0AAAAYZFRuNUtQMkJ5YWtKT1JnWUtCeXhmNmdP.l0T3G-i8I5dMwz7lEZnQAeK_WeqEZTxcDeyNY2poz_M"

iex(2)> [_, payload, _] = String.split(set_cookie, ".", parts: 3)
["SFMyNTY",
 "g3QAAAABbQAAAAtfY3NyZl90b2tlbm0AAAAYZFRuNUtQMkJ5YWtKT1JnWUtCeXhmNmdP",
 "l0T3G-i8I5dMwz7lEZnQAeK_WeqEZTxcDeyNY2poz_M"]

iex(3)> {:ok, encoded_term } = Base.url_decode64(payload, padding: false)
{:ok,
 <<131, 116, 0, 0, 0, 1, 109, 0, 0, 0, 11, 95, 99, 115, 114, 102, 95, 116, 111,
   107, 101, 110, 109, 0, 0, 0, 24, 100, 84, 110, 53, 75, 80, 50, 66, 121, 97,
   107, 74, 79, 82, 103, 89, 75, 66, 121, 120, 102, ...>>}

iex(4)> :erlang.binary_to_term(encoded_term)
%{"_csrf_token" => "dTn5KP2ByakJORgYKByxf6gO"}

Java示例

public static String decodePhoenixSessionCookie(String sessionCookie) {
    String payload = sessionCookie.split("\\.")[1];
    byte[] encoded_term = Base64.getUrlDecoder().decode(payload.getBytes());
    return new String(encoded_term);
}

Java输出

�tm_csrf_tokenmdTn5KP2ByakJORgYKByxf6gO

乱码原因

Phoenix Session的payload是Erlang Term二进制格式,并非纯文本:

  • Erlang Term有固定二进制结构:开头的<<131>>是Erlang Term的魔数,<<116>>表示这是Map类型,后续还有字段长度、字符串长度等二进制标识位。
  • IEx中的:erlang.binary_to_term/1是专门解析该格式的函数,会自动处理这些标识位,只提取有效键值对。
  • Java直接将二进制转成String时,那些非可打印的标识位(魔数、类型标记、长度字段)会被显示成乱码,仅其中的字符串内容(_csrf_token及对应值)是可打印的。

实现干净输出的方法

Java需要使用能解析Erlang Term格式的库,官方推荐JInterface(Erlang官方提供的Java交互库),或第三方库如erlang-term。以下是JInterface的实现示例:

1. 引入依赖(Maven)

<dependency>
    <groupId>com.ericsson.otp</groupId>
    <artifactId>erlang</artifactId>
    <version>2.2.1</version>
</dependency>

2. 解码代码

import com.ericsson.otp.erlang.*;
import java.util.Base64;

public class PhoenixSessionDecoder {
    public static OtpErlangMap decodePhoenixSession(String sessionCookie) throws OtpErlangDecodeException {
        // 拆分Cookie,获取中间的Payload部分
        String payload = sessionCookie.split("\\.")[1];
        // URL安全Base64解码得到Erlang Term二进制数据
        byte[] termBytes = Base64.getUrlDecoder().decode(payload);
        // 解析Erlang Term为Map结构
        OtpInputStream inputStream = new OtpInputStream(termBytes);
        return (OtpErlangMap) inputStream.read_any();
    }

    public static void main(String[] args) throws OtpErlangDecodeException {
        String cookie = "SFMyNTY.g3QAAAABbQAAAAtfY3NyZl90b2tlbm0AAAAYZFRuNUtQMkJ5YWtKT1JnWUtCeXhmNmdP.l0T3G-i8I5dMwz7lEZnQAeK_WeqEZTxcDeyNY2poz_M";
        OtpErlangMap sessionMap = decodePhoenixSession(cookie);
        
        // 输出指定键的值
        OtpErlangString csrfToken = (OtpErlangString) sessionMap.get(new OtpErlangString("_csrf_token"));
        System.out.println("_csrf_token: " + csrfToken.stringValue());
        
        // 遍历所有键值对
        System.out.println("===== 所有Session内容 =====");
        OtpErlangObject[] keys = sessionMap.keys();
        for (OtpErlangObject key : keys) {
            OtpErlangObject value = sessionMap.get(key);
            System.out.printf("%s: %s%n", ((OtpErlangString) key).stringValue(), ((OtpErlangString) value).stringValue());
        }
    }
}

输出结果

_csrf_token: dTn5KP2ByakJORgYKByxf6gO
===== 所有Session内容 =====
_csrf_token: dTn5KP2ByakJORgYKByxf6gO

这样就能得到和IEx一致的干净输出结果。

内容的提问来源于stack exchange,提问作者okan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 21:30:45