Перейти к основному содержимому
clawam tools
Инструменты Документы Daoark EN Главная Главная / Инструменты разработчика › Главная manifest.json /g, '>'); } function parseJwt(token) { var parts = token.split('.'); if (parts.length !== 3) throw new Error('JWT 必须有 3 段 (header.payload.signature)'); var headerJson, payloadJson, sigHex; try { var headerStr = b64UrlToStr(parts[0]); var payloadStr = b64UrlToStr(parts[1]); headerJson = JSON.parse(headerStr); payloadJson = JSON.parse(payloadStr); } catch (e) { throw new Error('Base64URL 解码失败: ' + e.message); } try { var sigBytes = b64UrlToBytes(parts[2]); sigHex = bytesToHex(sigBytes); } catch (e) { sigHex = '(解码失败)'; } return { header: headerJson, payload: payloadJson, signature: sigHex, parts: parts }; } function jsonToTable(obj) { if (obj === null || typeof obj !== 'object') return '' + escHtml(String(obj)) + ''; var html = ''; for (var k in obj) { if (!Object.prototype.hasOwnProperty.call(obj, k)) continue; var v = obj[k]; var type = Array.isArray(v) ? 'array[' + v.length + ']' : typeof v; var val = typeof v === 'string' ? '' + escHtml(v) + '' : escHtml(JSON.stringify(v, null, 2)); html += ''; } html += '
' + escHtml(k) + '' + type + ' ' + val + '
'; return html; } function checkTime(payload) { var now = Math.floor(Date.now() / 1000); var info = []; if (typeof payload.exp === 'number') { var expired = now >= payload.exp; var remain = payload.exp - now; info.push('exp' + new Date(payload.exp * 1000).toISOString() + ' (' + (expired ? '已过期 ' + (-remain) + ' 秒' : '剩余 ' + remain + ' 秒') + ')'); } if (typeof payload.nbf === 'number') { var valid = now >= payload.nbf; info.push('nbf' + new Date(payload.nbf * 1000).toISOString() + ' (' + (valid ? '已生效' : '未生效') + ')'); } if (typeof payload.iat === 'number') { info.push('iat' + new Date(payload.iat * 1000).toISOString() + ' (签发于)'); } return info.join(''); } // HMAC 验证 async function verifyHmac(headerB64, payloadB64, sigHex, secret, alg) { var data = new TextEncoder().encode(headerB64 + '.' + payloadB64); var keyData = new TextEncoder().encode(secret); var algo = { name: 'HMAC' }; if (alg === 'HS256') algo.hash = 'SHA-256'; else if (alg === 'HS384') algo.hash = 'SHA-384'; else if (alg === 'HS512') algo.hash = 'SHA-512'; else throw new Error('不支持的 HMAC 算法: ' + alg); var key = await crypto.subtle.importKey('raw', keyData, algo, false, ['sign']); var sigBuf = await crypto.subtle.sign('HMAC', key, data); var computed = bytesToHex(new Uint8Array(sigBuf)); // 时间恒定比较 if (computed.length !== sigHex.length) return false; var diff = 0; for (var i = 0; i < computed.length; i++) { diff |= computed.charCodeAt(i) ^ sigHex.charCodeAt(i); } return diff === 0; } function syncFields() { var op = getVal('op'); var grp = $('secret-group'); if (op === 'verify') { grp.hidden = false; grp.style.display = 'block'; } else { grp.hidden = true; grp.style.display = 'none'; } } function doProcess() { try { var op = getVal('op'); var token = getVal('text-a').trim(); if (!token) { showError('请输入 JWT'); return; } var parsed = parseJwt(token); var alg = parsed.header.alg; var html = '

Header

' + jsonToTable(parsed.header) + '

Payload

' + jsonToTable(parsed.payload) + '

时间检查

' + checkTime(parsed.payload) + '
' + '

Signature

' + escHtml(parsed.signature.substring(0, 64)) + (parsed.signature.length > 64 ? '...' : '') + '

完整长度 ' + parsed.signature.length + ' hex (' + (parsed.signature.length / 2) + ' 字节)

'; if (op === 'decode') { showResult(html, 'JWT 解码 | alg=' + alg + ', typ=' + (parsed.header.typ || 'N/A') + ' | 仅解码未验证签名'); } else if (op === 'verify') { var secret = getVal('secret'); if (!secret) { showError('请输入 secret'); return; } if (!/^HS(256|384|512)$/.test(alg)) { showError('本工具只支持 HS256/HS384/HS512 验证,当前 alg=' + alg + ' 不支持。已显示解码结果。'); $('result-area').hidden = false; $('result-area').style.display = 'block'; $('result-meta').textContent = 'JWT 解码(签名未验证) | alg=' + alg; return; } verifyHmac(parsed.parts[0], parsed.parts[1], parsed.signature, secret, alg).then(function(ok) { var verifyHtml = html + '

签名验证

' + (ok ? ' 签名有效' : ' 签名无效') + ' (' + alg + ')
'; showResult(verifyHtml, 'JWT 验证完成 | alg=' + alg + ' | ' + (ok ? '签名有效' : '签名无效(secret 不匹配)')); }).catch(function(e) { showError('验证失败: ' + (e.message || e)); }); } else { showError('未知操作'); } } catch (e) { showError('JWT 处理失败: ' + (e.message || e)); } } $('op').addEventListener('change', syncFields); syncFields(); $('btn-main').addEventListener('click', function(e) { e.preventDefault(); doProcess(); }); $('btn-clear').addEventListener('click', function() { var inputs = document.querySelectorAll('input,textarea,select'); for (var i = 0; i < inputs.length; i++) { if (inputs[i].tagName === 'SELECT') inputs[i].selectedIndex = 0; else inputs[i].value = ''; } var ra = $('result-area'); ra.hidden = true; ra.style.display = 'none'; var em = $('error-msg'); em.hidden = true; em.style.display = 'none'; syncFields(); }); })();