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

Perl实现Shopify Multipass Token生成时持续返回无效请求

Shopify Multipass Token生成问题排查(Perl实现)

问题背景

使用Perl生成Shopify Multipass Token时持续收到「无效请求」响应,已确认以下内容无误:

  • 客户数据格式与内容:{"email": "randomEmail@gmail.com", "last_name": "last_name", "first_name": "first_name"}
  • 店铺API密钥、店铺URL
  • 加密密钥与签名密钥的生成逻辑(基于API密钥SHA256哈希拆分)
  • UTC时间格式正确性

通过Python的spylib测试可生成有效Token,注释时间验证逻辑后,Perl生成的Token长度与有效Token一致,怀疑问题出在加密环节。

当前实现代码

sub generate_multipass_token {
    use JSON qw(encode_json);
    use MIME::Base64 qw(encode_base64);
    use Digest::SHA qw(sha256 hmac_sha256);
    use Crypt::CBC;
    use Crypt::Cipher::AES;
    use POSIX qw(strftime);
    use Encode qw(encode);

    my ($customer_data) = @_;

    my $store_api_key = '';
    my $store_url = '';

    my $hash = sha256($store_api_key);
    my $encryption_key = substr($hash, 0, 16);
    my $signing_key = substr($hash, 16, 16);

    my ($sec, $min, $hour, $day, $mon, $year) = gmtime();
    my $formatted_time = sprintf("%04d-%02d-%02dT%02d:%02d:%02dZ", $year+1900, $mon+1, $day, $hour, $min, $sec);

    # Add the formatted time to the customer data
    $customer_data->{created_at} = $formatted_time;

    # JSON-encode the customer data
    my $json_customer_data = encode_json($customer_data);

    # Encrypt the customer data using AES
    my $iv = Crypt::CBC->random_bytes(16);
    my $cipher = Crypt::CBC->new(
        -key     => $encryption_key,
        -iv      => $iv,
        -cipher  => 'Cipher::AES',
        -header  => 'none',
    );
    my $encrypted_data = $cipher->encrypt($json_customer_data);
    $encrypted_data = $iv . $encrypted_data;

    my $signature = hmac_sha256($encrypted_data, $signing_key);
    my $token = encode_base64url($encrypted_data . $signature);

    return "$store_url/account/login/multipass/" . $token;
}

sub encode_base64url  {
    my $data = shift;
    my $encoded = encode_base64($data, '');
    $encoded =~ tr{+/}{-_};
    $encoded =~ s/=+$//;
    return $encoded;
}

加密环节排查方向与修复建议

1. 显式指定AES填充与密钥参数

Shopify要求使用AES-128-CBC模式 + PKCS#7填充,原代码未显式指定填充规则,且Cipher模块名称有误,需调整加密初始化逻辑:

my $cipher = Crypt::CBC->new(
    -key        => $encryption_key,
    -iv         => $iv,
    -cipher     => 'Crypt::Cipher::AES', # 修正模块名称
    -header     => 'none',
    -padding    => 'pkcs7', # 显式指定PKCS7填充
    -literal_key => 1, # 直接使用原始密钥,不做额外哈希处理
    -keysize    => 16, # 明确AES-128密钥长度
);

2. 确保JSON编码为UTF-8格式

encode_json默认输出UTF-8,但显式编码可避免潜在字符集问题:

my $json_customer_data = encode('UTF-8', encode_json($customer_data));

3. 简化UTC时间生成逻辑

用strftime直接生成标准UTC时间,避免手动拼接可能的格式错误:

my $formatted_time = strftime("%Y-%m-%dT%H:%M:%SZ", gmtime());

修改后的完整代码

sub generate_multipass_token {
    use JSON qw(encode_json);
    use MIME::Base64 qw(encode_base64);
    use Digest::SHA qw(sha256 hmac_sha256);
    use Crypt::CBC;
    use Crypt::Cipher::AES;
    use POSIX qw(strftime);
    use Encode qw(encode);

    my ($customer_data) = @_;

    my $store_api_key = ''; # 填入你的店铺API密钥
    my $store_url = ''; # 填入你的店铺URL

    my $hash = sha256($store_api_key);
    my $encryption_key = substr($hash, 0, 16);
    my $signing_key = substr($hash, 16, 16);

    # 生成标准UTC时间
    my $formatted_time = strftime("%Y-%m-%dT%H:%M:%SZ", gmtime());

    # 添加时间参数到客户数据
    $customer_data->{created_at} = $formatted_time;

    # JSON编码并确保UTF-8格式
    my $json_customer_data = encode('UTF-8', encode_json($customer_data));

    # 初始化符合Shopify要求的AES-CBC加密器
    my $iv = Crypt::CBC->random_bytes(16);
    my $cipher = Crypt::CBC->new(
        -key        => $encryption_key,
        -iv         => $iv,
        -cipher     => 'Crypt::Cipher::AES',
        -header     => 'none',
        -padding    => 'pkcs7',
        -literal_key => 1,
        -keysize    => 16,
    );
    my $encrypted_data = $cipher->encrypt($json_customer_data);
    $encrypted_data = $iv . $encrypted_data;

    # 生成HMAC签名并拼接Token
    my $signature = hmac_sha256($encrypted_data, $signing_key);
    my $token = encode_base64url($encrypted_data . $signature);

    return "$store_url/account/login/multipass/" . $token;
}

sub encode_base64url  {
    my $data = shift;
    my $encoded = encode_base64($data, '');
    $encoded =~ tr{+/}{-_};
    $encoded =~ s/=+$//;
    return $encoded;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 23:42:30