# 1. 免密授权登录

# 请求URL

  • /api/jt/wallet/open/user/getLoginToken

# 请求方式

  • POST

# 请求头

参数名 必选 类型 说明
Content-Type string application/json

# 请求体参数

参数名 必选 类型 说明 示例(加密前) 示例(加密后)
appId string Application APPID
aesKey string AES Key (RSA加密后) "{\"key\": \"qwertasdfgzxcvbn\", \"iv\": \"bvcxzgfdsatrewqy\"}" YDPeqH0+MJiW5Jfi9CQXy/Btz4YkiZCOp5njAy5G5s87HaS/zgmQZBnDeNjFrSduAL1tC329XIJGX+u6SKZtRvtrCx5UhZGRviBBhA575mt99TcUqAVEjXRn+ShpZmg6U8N0IHl8TX8psyWiAzxIBkrDCqIlXODfNdr8/LgZcuQFgwQGurZ8QX9L9BZxBdOH1ksLqP0Dlr5awQYD9LAstA8nzZ9C9qXt7OD+XvE6qK/8pi9YYr02oqdaXyo2ypITz6HkXKibYSv6HDtPIy6h44Qgnc6psmE4bBmH7k6q27u0mqe3zFzXiZlF7NOIkl8inFKaR59jD06L/xbuM+oTOrKHThY7+FaObf1gVSRqrzucuv9gNgI2tZRkG5AzYCMTIER0zXfx2QtVUxmOvkl6+OAVBNnLC+N9KdzXSiUU1zehg3+VrosJqyNTfYT/8iY4NVa2IPzX5CN89lA3zYoDeiyBpzH71H86oU3xzoNQS00QrgWi0tK4WXaU+duPO+wLzfGv9/2G0I5Hgf2XkjFD+Ab8wWiDxJ0YfT1mBHtjkNtlvtrD4Sc48UClxE4vGMp4ekapd6+2yEzMYEVY4o/HUgE6QgeiGcE25yT8RC8yMxw4eHQnE4NTjwowdqm+Vq28iOw2IufRxjfcPyz7f9TgGGwhu/IHwh24exfJx73RKFA=
param string Parameter body (AES加密字符) "{\"timestamp\":1715595802,\"userId\":\"1095595801938341100\",\"mobile\":\"639123456778\"}" u+wP+tVHRCDT21oIC32X+RogU3x5blBdikNSvRFuM9TkWMtc9WrZKCX6YIS1uezJZXR925qEBR6oVyCQ9tO6t7d0Z3xm/wKlufx2hQEfRbE=
sign string Signature (参数-param使用加密前) EJeMHGDlLu3DdbIK/52aID1soLrD0rfGcEEsAQ8cXFWaXQHioVwfZUD82U5g67NTXP/F/0Mhg6bK7n6rJlC/clYzqZ1kHmO2FbCdujw0ATY+FfM6VkgVT4pXr1jYmg3Xe23RDqhFvkDcOfRvaGPtnf64yrQG32MZps23TsbnafitDbtfqSoDIZkDCw1j1EH3Shhd64xKh9L3O8ivyF2UDOiHSAD8+18JXOxMWMFcR01STKE5B+zlEu5OA98+ClDBdroekBVKDeBeagwwQr+0zDFUvAb+2QIPKA2o0JM3ooituiaKVCgCVfndNjk8wIzKz2QYp+RhjVOC/u+8+Yoqb4KoWjGkMXgDbM1GZpdHG/BmzqXqhnNF0Y0LYS4bkITstlOm30nnHSljT5bxjk2Sq1hp9259SqMdp52OyoznxbRwEva5Wk5YPo+zD8IHOctEjkwWXz9/jo2evK6QcWCOH2LM6cG7EoP6Cg+5P2AUTofSMZsn/ZnAy6h9MXIuirDBiujrKMk85EY74s8i7yCNmjQ5NRbPbKsNTJbkxp2cRSMPzYPzOXXqEgKMmYzVQ5SSAcqFZIj2fAOL/3fG+2aEMn/8AFjJuD3/160BmYCZbAY4A2LJow6SGP/OcifvWPwoziJv0IP5Jff59w0mv6h0+3HnJg5YAcqK7dani1C0cAQ=

# param 解密之后参数

参数名 必选 类型 长度 示例 说明
timestamp long - 1677495496979 毫秒时间戳
userId string 1-32 - 极兔用户uuid唯一标识号
mobile string - - 639开头,共12位数字
terminalType string 20 PC 设备类型,枚举值:PC, 默认值: PC
isIframe string 20 N 是否需要在iframe中打开网页,枚举值:YN,当设备类型为PC时该值生效。如果需要设置为Y,否则设置为N

# 请求示例

# 请求体

{
  "appId": "733b887a4a784708bb369524db5b6ded",
  "aesKey": "YDPeqH0+MJiW5Jfi9CQXy/Btz4YkiZCOp5njAy5G5s87HaS/zgmQZBnDeNjFrSduAL1tC329XIJGX+u6SKZtRvtrCx5UhZGRviBBhA575mt99TcUqAVEjXRn+ShpZmg6U8N0IHl8TX8psyWiAzxIBkrDCqIlXODfNdr8/LgZcuQFgwQGurZ8QX9L9BZxBdOH1ksLqP0Dlr5awQYD9LAstA8nzZ9C9qXt7OD+XvE6qK/8pi9YYr02oqdaXyo2ypITz6HkXKibYSv6HDtPIy6h44Qgnc6psmE4bBmH7k6q27u0mqe3zFzXiZlF7NOIkl8inFKaR59jD06L/xbuM+oTOrKHThY7+FaObf1gVSRqrzucuv9gNgI2tZRkG5AzYCMTIER0zXfx2QtVUxmOvkl6+OAVBNnLC+N9KdzXSiUU1zehg3+VrosJqyNTfYT/8iY4NVa2IPzX5CN89lA3zYoDeiyBpzH71H86oU3xzoNQS00QrgWi0tK4WXaU+duPO+wLzfGv9/2G0I5Hgf2XkjFD+Ab8wWiDxJ0YfT1mBHtjkNtlvtrD4Sc48UClxE4vGMp4ekapd6+2yEzMYEVY4o/HUgE6QgeiGcE25yT8RC8yMxw4eHQnE4NTjwowdqm+Vq28iOw2IufRxjfcPyz7f9TgGGwhu/IHwh24exfJx73RKFA=",
  "sign": "EJeMHGDlLu3DdbIK/52aID1soLrD0rfGcEEsAQ8cXFWaXQHioVwfZUD82U5g67NTXP/F/0Mhg6bK7n6rJlC/clYzqZ1kHmO2FbCdujw0ATY+FfM6VkgVT4pXr1jYmg3Xe23RDqhFvkDcOfRvaGPtnf64yrQG32MZps23TsbnafitDbtfqSoDIZkDCw1j1EH3Shhd64xKh9L3O8ivyF2UDOiHSAD8+18JXOxMWMFcR01STKE5B+zlEu5OA98+ClDBdroekBVKDeBeagwwQr+0zDFUvAb+2QIPKA2o0JM3ooituiaKVCgCVfndNjk8wIzKz2QYp+RhjVOC/u+8+Yoqb4KoWjGkMXgDbM1GZpdHG/BmzqXqhnNF0Y0LYS4bkITstlOm30nnHSljT5bxjk2Sq1hp9259SqMdp52OyoznxbRwEva5Wk5YPo+zD8IHOctEjkwWXz9/jo2evK6QcWCOH2LM6cG7EoP6Cg+5P2AUTofSMZsn/ZnAy6h9MXIuirDBiujrKMk85EY74s8i7yCNmjQ5NRbPbKsNTJbkxp2cRSMPzYPzOXXqEgKMmYzVQ5SSAcqFZIj2fAOL/3fG+2aEMn/8AFjJuD3/160BmYCZbAY4A2LJow6SGP/OcifvWPwoziJv0IP5Jff59w0mv6h0+3HnJg5YAcqK7dani1C0cAQ=",
  "param": "u+wP+tVHRCDT21oIC32X+RogU3x5blBdikNSvRFuM9TkWMtc9WrZKCX6YIS1uezJZXR925qEBR6oVyCQ9tO6t7d0Z3xm/wKlufx2hQEfRbE="
}

// 解密后
"aesKey": "{\"key\": \"qwertasdfgzxcvbn\", \"iv\": \"bvcxzgfdsatrewqy\"}"
"param": "{\"timestamp\":1715595802,\"userId\":\"1095595801938341100\",\"mobile\":\"639123456778\"}"

# Rsa 加签与AES加密工具类

import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class SignAndEncryptUtil {
    
    /**
     * 
     * @param param Param JsonString
     */
    public static boolean verifySign(String param, String sign, String publicKey) throws NoSuchAlgorithmException, SignatureException, InvalidKeySpecException, InvalidKeyException {
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initVerify(KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey))));
        signature.update(param.getBytes());
        return signature.verify(Base64.getDecoder().decode(sign));
    }

    /**
     * 
     * @param param Param JsonString
     * @return  sign
     */
    public static String generateSign(String param, String privateKey) throws NoSuchAlgorithmException, SignatureException, InvalidKeySpecException, InvalidKeyException {
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey))));
        signature.update(param.getBytes());
        byte[] bytes = signature.sign();
        return Base64.getEncoder().encodeToString(bytes);
    }

    public static String encryptWithPrivateKey(String data, String privateKeyStr) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(privateKeyStr);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey =keyFactory.generatePrivate(keySpec);
        
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        byte[] encryptedBytes = cipher.doFinal(data.getBytes());
        
        return Base64.getEncoder().encodeToString(encryptedBytes);
    }

    
    public static String decryptWithPublicKey(String encryptedData, String publicKeyStr) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(publicKeyStr);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey =  keyFactory.generatePublic(keySpec);

        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        byte[] decodedBytes = Base64.getDecoder().decode(encryptedData);
        return new String(cipher.doFinal(decodedBytes));
    }


    public static final String PRIVATE_KEY = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCV+0mJmN5wOrfcPuAF6JKHQg066WX4/QYOAbhFe/yPJ+tu5QfKau9yptJcwenwV8Mj5DM1QWBAY/d0ThYpRkuPjL9zjWuOzrWFfMuB7EktY13a9F32QlnGJ4JIW5PAoRXl/fsbeZQbrKxnAHvZqOl/LFpNA/eSz4TZ2EegIfILFjTgOnL+g1ZgRiSRBk4DVhDtdDhwvwb0BBK1TAMV0btBJIykcNR7YYAG2+rMwpgtSlDkKB9KuSdtG72GeyAwRqMJJrl77+m1XfR9Py2r8cQ/O3Og7wzE/V1gFhwvZADludFZhgP8pskr9j1gJmU58S1+Xz9Bv5cdLFinzyNNadpTAgMBAAECggEAA3JM1E6g/e87fmIrf7dCdr071Ji8cSZPV08Ozvn0ac2/CUNWFH60levjdaI3IADESTTbQGQKNDCX5SJOPBCTd+8CD0O2rwdtAG5HtuqZG+PmqjtnVVtc+MK7qbIBCTIqcKiPdqgqkSA8rflC31OUWgnI4XAv5j3Cjcb8jl95UmvdNXYQlLeaDnFG24pf1pgbExJndM0fjz9bxFNHVYGa6RxCwbrNLdsKgB2sNg5dKxmZw7JvZtmCcDnb1LTC2sftJJVds4NlmMbWfTS8NVIQa3mKSuwD0jV79xQa+Ec7V6xsod73jz3nbAcKhTPN5B6DJKYtO+aqQKIUlmbSYHAiAQKBgQDHGIX7gIAqIP2gtOpmsvi1mRJeh9F4psaLGU3DMFSAl/Ht/Zs/SYP70mHFYtYvUEjLgpw+V9W48f7mRuAZoRJCKMlKkXlCfCqTImioXUXiPsOBeuglewwmVzw/hOVVh5v+xwIbDu6P8IexXtmAVroBwO3Jdxutsat79iGAr9H80wKBgQDA2SrFNpY/9UhLSQfzY2mm8Yng2Cu1rPTxZRRn4T/2i2vCArPFW/wNspPttEengnE5r7TUJr/a+jZUQz6gpSuNU4eKpOwYZ7QD2szC1UEs39Z/f7pIe7JKUUDdnubECCSR7Vs1uiKFvOILdCf9t/hmsq3+yaM0rGG3jhTa1kg8gQKBgQCZRykHanPBe2qoCgCYFlthu7onqtq6z3L1bgKvLgswgUpRljiqbZe+DMcW5rPM8ztH6oMNgCPbYfuDH7eyI45h/vKRhRPc+qjwY8I9NKnzt6xeh9gn2uaMsgaBRHgm8+NGL8JQQJurl1twU3yR0LMdXIH0480cWTeTlPdQEoxQ3wKBgQCs7U+j/lnmyjzkfX1qq3de4vJfM5imVYxjNd+BcwOXQdgD5cZsMBqp8bsiiOlD9P0w3DULlB+v7XRwRMhQ5ytRlFhTY6XuEHfkiPvZrb6+zY0bUV6qhnkOmSPQVx/ZIvAgBrYmTF2OjRwpZyRBKAnPlWPet9Lodbc+EGL1BPdLgQKBgBSUxIChyE3oo4rAMzCenOtywvyrOhjc5v4UrlvyhWuqej3T9rQ7NMDG0ba+F/guqj3ZruePNsU24X6nd1uiJvHW8XdvtZuGIfDDMOzFgn0G3m9hSXQNTCwaaaQaeuE7G5z1S+x07s6jyv2LfOBHwRuhOshg0bz96jpOIUkKipYH";
    public static final String PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlftJiZjecDq33D7gBeiSh0INOull+P0GDgG4RXv8jyfrbuUHymrvcqbSXMHp8FfDI+QzNUFgQGP3dE4WKUZLj4y/c41rjs61hXzLgexJLWNd2vRd9kJZxieCSFuTwKEV5f37G3mUG6ysZwB72ajpfyxaTQP3ks+E2dhHoCHyCxY04Dpy/oNWYEYkkQZOA1YQ7XQ4cL8G9AQStUwDFdG7QSSMpHDUe2GABtvqzMKYLUpQ5CgfSrknbRu9hnsgMEajCSa5e+/ptV30fT8tq/HEPztzoO8MxP1dYBYcL2QA5bnRWYYD/KbJK/Y9YCZlOfEtfl8/Qb+XHSxYp88jTWnaUwIDAQAB";

    /**
     * main方法:演示签名验证和加密解密
     */
    public static void main(String[] args) {
        try {
            // 测试数据
            String param = "{\"timestamp\":1715595802,\"userId\":\"1095595801938341100\",\"mobile\":\"639123456778\"}";
            String aesKey = "{\"key\": \"qwertasdfgzxcvbn\", \"iv\": \"bvcxzgfdsatrewqy\"}";

            System.out.println("\n========== 原始数据 ==========");
            System.out.println("param: " + param);
            System.out.println("aesKey: " + aesKey);

            // 0. 使用AES加密param内容
            System.out.println("\n========== 0. AES加密与解密 ==========");
            JSONObject aesKeyJson = JSON.parseObject(aesKey);
            String key = aesKeyJson.getString("key");
            String iv = aesKeyJson.getString("iv");

            // 创建AES加密器 (CBC模式, PKCS5Padding填充)
            AES aes = new AES(Mode.CBC, Padding.PKCS5Padding, key.getBytes(), iv.getBytes());

            // 加密param
            String encryptedParam = aes.encryptBase64(param);
            System.out.println("AES加密后的param: " + encryptedParam);

            // 解密验证
            String decryptedParam = aes.decryptStr(encryptedParam);
            System.out.println("AES解密后的param: " + decryptedParam);
            System.out.println("AES加解密验证: " + (param.equals(decryptedParam) ? "✓ 成功" : "✗ 失败"));

            // 1. 使用RSA私钥对param进行加签,获得签名并验证
            System.out.println("\n========== 1. RSA签名与验证 ==========");
            String sign = generateSign(param, PRIVATE_KEY);
            System.out.println("生成的签名: " + sign);

            boolean verifyResult = verifySign(param, sign, PUBLIC_KEY);
            System.out.println("签名验证结果: " + (verifyResult ? "✓ 通过" : "✗ 失败"));

            // 2. 使用RSA私钥对aesKey进行加密,获得加密数据并验证
            System.out.println("\n========== 2. RSA加密与解密 ==========");
            String encryptedAesKey = encryptWithPrivateKey(aesKey, PRIVATE_KEY);
            System.out.println("加密后的aesKey: " + encryptedAesKey);

            String decryptedAesKey = decryptWithPublicKey(encryptedAesKey, PUBLIC_KEY);
            System.out.println("解密后的aesKey: " + decryptedAesKey);
            System.out.println("解密验证: " + (aesKey.equals(decryptedAesKey) ? "✓ 成功" : "✗ 失败"));

        } catch (Exception e) {
            System.err.println("发生错误: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

# 响应参数

参数名 类型 说明
code integer response code
message string response message
data Object data

# data参数

参数名 类型 说明
expireSeconds int 3600秒(过期时间)
authUrl string 免密登录地址
accountNo string 钱包用户账号(钱包侧用户唯一标识号)
token string 钱包用户登录token

# 响应示例

# 响应数据体

{
  "code": 1000,
  "message": "success",
  "data": {
    "expireSeconds": 3600,
    "authUrl": "https://www.paycools.com.ph/#/splash?token=V0xUX1VTRVI6YTBiMjliZmItMzAxMy00ZWVjLTkzY2QtMGRiMzJjYjUxNmUx",
    "accountNo": "2200636699666629",
    "token": "V0xUX1VTRVI6ZjQ4NjdkYjQtYzAxYy00OTNmLWJhODEtM2Q3MWU1NTM3NmFm"
  }
}

# 返回失败案例 响应code列表

{
  "code":1002,
  "message":"merchant white ip forbidden"
}