SOURCE

const secretKey = 'bbcbc743fa6297659f2505bd4958ba8eb2cb16a4baecab4f88eae3f4665a4601';

async function test() {
    const jwt = await getJWT('qinleilxl@outlook.com');
    console.log('>>>>>> jwt: ', jwt);
    const [err, isValid] = await verifyJWT("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJxaW5sZWlseGxAZ21haWwuY29tIiwiZXhwaXJlIjoxNjk3MDY4ODAwMDAwLCJyYW5kb20iOiI1NTA3ODU0NTc5NzMyMTBlZDYzYTkzOWRiMzk5N2JkYyJ9.3bXgftD90TUSfzduUo7VRzG4zaUm10A654DgoPGXUw=", secretKey);
    if (err) {
    console.log('>>>>> err: ', err);
    }
    console.log('>>>>> isValid: ', isValid);
}
test();

// 验证jwt合法性
function verifyJWT(jwt, secretKey) {
    // 拆分 JWT 为头部、负载和签名部分
    const [header, payload, signature] = jwt.split('.');

    // 解码头部和负载部分
    const decodedHeader = atob(header);
    const decodedPayload = atob(payload);

    // 使用头部中的算法信息来确定签名算法
    const headerObj = JSON.parse(decodedHeader);
    const algorithm = headerObj.alg;

    // 重新计算签名
    const encodedSignature = btoa(signature);
    const dataToVerify = `${header}.${payload}`;
    const encoder = new TextEncoder();
    const dataToVerifyBuffer = encoder.encode(dataToVerify);

    // const importedKey = await crypto.subtle.importKey(
    //     'raw',
    //     encoder.encode(secretKey),
    //     { name: 'HMAC', hash: { name: 'SHA-256' } },
    //     false,
    //     ['verify']
    // );
    // const isValid = await crypto.subtle.verify(
    //     'HMAC',
    //     importedKey,
    //     new Uint8Array(atob(signature).split('').map(char => char.charCodeAt(0))),
    //     new Uint8Array(dataToVerifyBuffer).buffer,
    // );
    // return isValid;

    // 使用密钥和签名算法来验证签名
    return crypto.subtle.importKey(
        'raw',
        encoder.encode(secretKey),
        { name: 'HMAC', hash: { name: 'SHA-256' } },
        false,
        ['verify']
    )
        .then((importedKey) => {
            return crypto.subtle.verify(
                'HMAC',
                importedKey,
                new Uint8Array(atob(signature).split('').map(char => char.charCodeAt(0))),
                new Uint8Array(dataToVerifyBuffer).buffer,
            );
        })
        .then((isValid) => {
            return [null, isValid];
        })
        .catch((error) => {
            console.error('验证 JWT 签名时出错:', error.message);
            return [error.message, false];
        });
}

// 解析jwt
function decodeJWT(jwt) {
    const parts = jwt.split('.');
    if (parts.length !== 3) {
        throw new Error('Invalid JWT format');
    }

    const encodedPayload = parts[1];
    const decodedPayload = atob(encodedPayload);
    const payloadObj = JSON.parse(decodedPayload);

    return payloadObj;
}

async function getJWT(userId) {
    const now = new Date();
    const expireDay = customAdd(now, 1, 'day');
    const expireTime = getStartOfDay(expireDay).getTime();
    const payload = {
        userId,
        expire: expireTime,
        random: generateRandom32BitString()
    };

    // 设置 JWT 的头部
    const header = {
        alg: 'HS256',
        typ: 'JWT',
    };

    // 创建 JWT 的编码头部
    const encodedHeader = btoa(JSON.stringify(header));

    // 创建 JWT 的编码负载
    const encodedPayload = btoa(JSON.stringify(payload));

    // 构建用于签署的数据字符串,包括编码头部和编码负载,中间用 "." 分隔
    const dataToSign = `${encodedHeader}.${encodedPayload}`;

    // 使用密钥进行 HMAC SHA-256 签名
    const signature = await signWithHmacSHA256(dataToSign, secretKey);

    // 构建最终的 JWT
    const jwt = `${dataToSign}.${signature}`;
    return jwt;
}

// 日期增加
function customAdd(oldDate, amount, unit) {
    if (!isValidDate(oldDate)) {
        throw new Error('Invalid date');
    }
    const date = new Date(oldDate);
    switch (unit) {
        case 'year':
            date.setFullYear(date.getFullYear() + amount);
            break;
        case 'month':
            date.setMonth(date.getMonth() + amount);
            break;
        case 'day':
            date.setDate(date.getDate() + amount);
            break;
        case 'hour':
            date.setHours(date.getHours() + amount);
            break;
        case 'minute':
            date.setMinutes(date.getMinutes() + amount);
            break;
        case 'second':
            date.setSeconds(date.getSeconds() + amount);
            break;
        default:
            throw new Error(`Invalid unit: ${unit}`);
    }

    return date;
}

// 日期合法性校验
function isValidDate(date) {
    return date instanceof Date && !isNaN(date);
}

// 获取日期这一天的开始时间
function getStartOfDay(date) {
    date.setHours(0, 0, 0, 0); // 设置时间为午夜
    return date;
}

// 使用 HMAC SHA-256 签名数据
async function signWithHmacSHA256(data, key) {
    const encoder = new TextEncoder();
    const dataBuffer = encoder.encode(data);
    const keyBuffer = encoder.encode(key);
    // 使用 crypto.subtle.importKey 导入密钥
    const importedKey = await crypto.subtle.importKey(
        'raw',
        keyBuffer,
        { name: 'HMAC', hash: 'SHA-256' },
        false,
        ['sign']
    );
    const signatureBuffer = await crypto.subtle.sign('HMAC', importedKey, dataBuffer);
    const verifyImportedKey = await crypto.subtle.importKey(
        'raw',
        encoder.encode(secretKey),
        { name: 'HMAC', hash: { name: 'SHA-256' } },
        false,
        ['verify']
    )
    const signatureArray = Array.from(new Uint8Array(signatureBuffer));
    const signatureHex = signatureArray.map(byte => byte.toString(16).padStart(2, '0')).join('');
    const signature = btoa(String.fromCharCode.apply(null, signatureArray));
    return signature;
}

// 生成32位随机字符串
function generateRandom32BitString() {
    // 生成16个随机字节
    const randomBytes = new Uint8Array(16);
    crypto.getRandomValues(randomBytes);

    // 将字节转换为十六进制字符串
    let hexString = '';
    for (let i = 0; i < randomBytes.length; i++) {
        let hex = randomBytes[i].toString(16);
        if (hex.length === 1) {
            hex = '0' + hex; // 确保每个字节都是两位十六进制
        }
        hexString += hex;
    }

    return hexString;
}

//eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiO…NCJ9.u0TtSwPMPeuH xj ggNj58/SZOJJb7/kLD2V/9K7mSs=
console 命令行工具 X clear

                    
>
console