ChatGPT解决这个技术问题 Extra ChatGPT

如何在不使用库的情况下在 javascript 中解码 jwt 令牌?

如何使用 JavaScript 解码 JWT 的有效负载?没有图书馆。所以令牌只返回一个可以被我的前端应用程序使用的有效负载对象。

示例令牌:xxxxxxxxx.XXXXXXXX.xxxxxxxx

结果是有效载荷:

{exp: 10012016 name: john doe, scope:['admin']}
它是如何编码的?只是做相反的事情。您将需要共享密钥。
您可以尝试访问 jwt.io 网站并获取它提供的 JavaScript 库。
由于这个问题有一些流量,我想添加一个免责声明:如果您盲目地解码令牌的有效负载,而不验证签名,您可能(或可能不会)遇到安全问题!在盲目使用此 stackoverflow 问题中提供的任何代码之前,请确保您了解您的安全架构。
@CarstenHoffmann 我该如何验证签名?
@SaurabhTiwari 通常,您作为客户无法验证签名。有关 JWT 是什么、签名和编码之间的区别以及打算如何使用的说明,请参阅此处 stackoverflow.com/questions/59632301/

m
mb21

注意:这不会验证签名,它只是从令牌中提取 JSON 有效负载,这可能已被篡改。

浏览器

工作 unicode 文本 JWT 解析器功能:

function parseJwt (token) {
    var base64Url = token.split('.')[1];
    var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
    var jsonPayload = decodeURIComponent(window.atob(base64).split('').map(function(c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
    }).join(''));

    return JSON.parse(jsonPayload);
};

JWT 使用 base64url(RFC 4648 §5),因此仅使用 atob(使用 base64)是不够的。

节点.js

function parseJwt (token) {
    return JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
}

该解决方案甚至可以在 Postman(测试点击)中使用,因为它不需要任何附加库安装。我用它从 auth-token 中提取用户 ID。
注意:在 Postman 中,我必须从 JSON.parse(window.atob(base64)) 中删除“窗口”才能使其正常工作。在我的情况下,只有 return JSON.parse(atob(base64)); 然后 postman.setEnvironmentVariable("userId", parseJwt(jsonData.access_token)); “access_token” 是令牌响应值的键(在您的情况下可能会有所不同)。
最好使用 jwt-decode 模块,因为它很小,但处理起来更好一些。
也许对某些人来说微不足道,但不要忘记通过添加 const atob = require('atob'); 来添加 atob 作为依赖项。
如果您使用的是 NodeJS,并且无法访问 atob,请改用:Buffer.from(base64, 'base64').toString()
R
Rajan Maharjan

简单的try-catch函数

const parseJwt = (token) => {
  try {
    return JSON.parse(atob(token.split('.')[1]));
  } catch (e) {
    return null;
  }
};

谢谢!


atob 知道 unicode problems
JWT uses base64url (RFC 4648 §5)。这个答案使用base64。这个答案是错误的。
@Pang atob() 和 btoa() 使用的算法在 RFC 4648 中指定。链接:developer.mozilla.org/en-US/docs/Glossary/Base64
@RajanMaharjan MDN page 说:“atob() 和 btoa() 使用的算法在 RFC 4648, section 4”中指定”,但 JWT(JWS) 使用定义在RFC 4648, Section 5。编码不一样。另见 wikipedia 上的 Base64 - Variants summary table
尝试使用firebase令牌,它不起作用
G
Guy

您可以使用 jwt-decode,因此您可以编写:

import jwt_decode from 'jwt-decode';

var token = 'eyJ0eXAiO.../// jwt token';

var decoded = jwt_decode(token);
console.log(decoded);
/*{exp: 10012016 name: john doe, scope:['admin']}*/

“我的意思是没有图书馆。”
他们是这个图书馆的问题。主要是配合firefox在用。我遇到的问题是,如果一个令牌 == null 是由于注销或过期而导致的;这只是以错误杀死页面。
@ApertureSecurity 你需要捕捉到这个错误,但不可否认这就是我不想使用这个库的原因
这似乎不支持 GZIP。事实上,我找不到任何支持 GZIP 声明的 JS 库。
M
Muhammed Moussa

您可以使用纯 javascript atob() 函数将令牌解码为字符串:

atob(token.split('.')[1]);

或者直接解析成一个json对象:

JSON.parse(atob(token.split('.')[1]));

了解 atob()btoa() 内置 javascript 函数Base64 encoding and decoding - Web APIs | MDN


JWT uses base64url (RFC 4648 §5)。这个答案使用base64。这个答案是错误的。
A
Adam
function parseJwt(token) {
  var base64Payload = token.split('.')[1];
  var payload = Buffer.from(base64Payload, 'base64');
  return JSON.parse(payload.toString());
}
let payload= parseJwt("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c");
console.log("payload:- ", payload);

如果使用节点,您可能必须使用缓冲包:

npm install buffer
var Buffer = require('buffer/').Buffer

我使用它而不是 atob,因为它已被弃用。
这对于 Node.js 来说是一个很好的答案。但是在浏览器中,Buffer 不存在(您的框架可能会添加一个大的 polyfill),而 atob 仅在 Node.js 上被弃用,在浏览器中使用非常好。
A
Avik

由于 nodejs 环境中不存在“window”对象,我们可以使用以下代码行:

let base64Url = token.split('.')[1]; // token you get
let base64 = base64Url.replace('-', '+').replace('_', '/');
let decodedData = JSON.parse(Buffer.from(base64, 'base64').toString('binary'));

它完美地为我工作。希望能帮助到你。


你为什么要做.toString('binary')?如果令牌中有特殊字符,这对我来说会很麻烦......虽然使用 .toString() 有效......
m
maninak

如果您使用的是 Typescript 或 vanilla JavaScript,那么这是一个零依赖,可以复制粘贴到您的项目简单函数中(基于 @Rajan Maharjan 的回答)。

这个答案特别好,不仅因为它不依赖于任何 npm 模块,还因为它不依赖于这里的一些其他解决方案正在使用的任何 node.js 内置模块(如Buffer),当然会在浏览器中失败(除非 polyfill,但首先没有理由这样做)。此外 JSON.parse 可能在运行时失败,这个版本(尤其是在 Typescript 中)将强制处理。 JSDoc 注释将使您的代码的未来维护者心存感激。 :)

/**
 * Returns a JS object representation of a Javascript Web Token from its common encoded
 * string form.
 *
 * @template T the expected shape of the parsed token
 * @param {string} token a Javascript Web Token in base64 encoded, `.` separated form
 * @returns {(T | undefined)} an object-representation of the token
 * or undefined if parsing failed
 */
export function getParsedJwt<T extends object = { [k: string]: string | number }>(
  token: string,
): T | undefined {
  try {
    return JSON.parse(atob(token.split('.')[1]))
  } catch {
    return undefined
  }
}

为了完成,这里也是 vanilla javascript 版本:

/**
 * Returns a JS object representation of a Javascript Web Token from its common encoded
 * string form.
 *
 * @param {string} token a Javascript Web Token in base64 encoded, `.` separated form
 * @returns {(object | undefined)} an object-representation of the token
 * or undefined if parsing failed
 */
export function getParsedJwt(token) {
  try {
    return JSON.parse(atob(token.split('.')[1]))
  } catch (error) {
    return undefined
  }
}

1. 这并没有对 JWT 的所有部分进行编码,而只是对其中一个部分进行了编码:“Payload”被解码,而“Header”和“Signature”被丢弃。这在文档中值得注意。 2. 返回类型限制性太强,因为它只允许字符串和数字作为值,但是 RFC 7519, Section 3.1 中的例子。使用布尔值,说明问题。 3. 您的实现不检查 JWT 的签名是否与其有效负载匹配。这可能会出现问题,具体取决于您的函数的使用方式。这应该在文档中提及,因为它会带来安全风险。
@LorenzLeutgeb 我希望在 github 问题中看到所有有效的评论,该问题是在 NPM 上发布的一个库,旨在解决 JWT 解码/解析问题。鉴于这个 Stack Overflow 问题上的人很可能在寻找 copy-pasta 脚本而不是库,我尽我所能平衡改进我在这里看到的所有其他答案,同时保持代码的可读性和可扩展性/可维护性。干杯,感谢您花时间澄清这些事情。
在 JWT 声明中使用 UTF-8 字符对我来说失败了。
然而,这个解决方案不是零依赖。它利用了 atob,它不是 Javascript 的一部分,而是一个 Web API。支持还不错,浏览器都支持。 Node 从 v16 开始支持它(这意味着当前有运行 Node 14 的主机,例如 AWS Lambda,它不支持它)。
m
mesqueeb

如果您使用 Node.JS,您可以通过执行以下操作来使用本机 Buffer 模块:

const token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImp0aSI6ImU3YjQ0Mjc4LTZlZDYtNDJlZC05MTZmLWFjZDQzNzhkM2U0YSIsImlhdCI6MTU5NTg3NzUxOCwiZXhwIjoxNTk1ODgxMTE4fQ.WXyDlDMMSJAjOFF9oAU9JrRHg2wio-WolWAkAaY3kg4';
const tokenDecodablePart = token.split('.')[1];
const decoded = Buffer.from(tokenDecodablePart, 'base64').toString();
console.log(decoded)

你很高兴去:-)


PS:此令牌是使用 jsonwebtoken.io 制作的
将此缩短为 JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()) 以直接获取 JS 对象。
R
Rafael Quintela

@Peheje 会工作,但你会遇到 unicode 问题。为了修复它,我使用 https://stackoverflow.com/a/30106551/5277071 上的代码;

让 b64DecodeUnicode = str => decodeURIComponent( Array.prototype.map.call(atob(str), c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2 ) ).join('')) 让 parseJwt = token => JSON.parse( b64DecodeUnicode( token.split('.')[1].replace('-', '+').replace('_', '/') ) ) 让 form = document.getElementById("form") form.addEventListener("submit", (e) => { form.out.value = JSON.stringify( parseJwt(form.jwt.value) ) e.preventDefault(); }) textarea{width:300px;高度:60px; display:block}


+1,但如果 Racing Tadpole 对 Peheje 的回答的评论是正确的(替换调用只会替换第一个实例),那么同样的修复将适用于此处。
S
Softmixt

我使用此函数根据 this 答案获取 payload 、 header 、 exp(Expiration Time)、 iat (Issued At)

function parseJwt(token) {
  try {
    // Get Token Header
    const base64HeaderUrl = token.split('.')[0];
    const base64Header = base64HeaderUrl.replace('-', '+').replace('_', '/');
    const headerData = JSON.parse(window.atob(base64Header));

    // Get Token payload and date's
    const base64Url = token.split('.')[1];
    const base64 = base64Url.replace('-', '+').replace('_', '/');
    const dataJWT = JSON.parse(window.atob(base64));
    dataJWT.header = headerData;

// TODO: add expiration at check ...


    return dataJWT;
  } catch (err) {
    return false;
  }
}

const jwtDecoded = parseJwt('YOUR_TOKEN') ;
if(jwtDecoded)
{
    console.log(jwtDecoded)
}

这个答案要好一些,但它有两个半问题。首先,它不检查签名(数组项 2)。其次,REPLACE 无法正常工作,因为它们错过了正则表达式上的“g”标志(只会替换 JWT 上第一次出现的 - 和 _,就像 Racing Tadpole 在另一篇文章中评论的那样)。还有一半:要解码数组项 0 和 1,您可以使用 FOR 循环,而不是复制整个代码(这是一个简短的代码,但可以提高效率,因为它的方式是 SPLIT 执行两次)。
N
Nao Ito

我在 jwt.io 找到了这段代码,它运行良好。

//this is used to parse base64
function url_base64_decode(str) {
  var output = str.replace(/-/g, '+').replace(/_/g, '/');
  switch (output.length % 4) {
    case 0:
      break;
    case 2:
      output += '==';
      break;
    case 3:
      output += '=';
      break;
    default:
      throw 'Illegal base64url string!';
  }
  var result = window.atob(output); //polifyll https://github.com/davidchambers/Base64.js
  try{
    return decodeURIComponent(escape(result));
  } catch (err) {
    return result;
  }
}

在某些情况下(某些开发平台),
the best answer(for now) 面临 base64 长度无效的问题。
所以,我需要一种更稳定的方法。

我希望它会帮助你。


V
Vasyl Boroviak

如果使用 node.js 16 或更高版本,您可以使用内置的 base64url 编码器/解码器。

let payload = JSON.parse(Buffer.from(token.split(".")[1], "base64url"));

这也适用于 Node v14.19.1。
d
drordk

您可以定义和使用这一个线性函数:

jwtDecode = b => JSON.parse(Buffer.from(b.split('.')[1], 'base64').toString('binary'));

J
Jithin Vijayan

jwt.io 的所有功能不支持所有语言。在 NodeJs 中,您可以使用

var decoded = jwt.decode(token);

如果没有库,您只需在令牌的第二部分执行 base64 解码 { var payload = token.split('.')[1]); } 然后执行 base64 解码 { var decodedData = atob(payload); }
c
calingasan

基于来自 GitHub - auth0/jwt-decode 的答案。更改了输入/输出以包括字符串拆分和返回对象 { header, payload, signature },这样您就可以传递整个令牌。

var jwtDecode = function (jwt) {

        function b64DecodeUnicode(str) {
            return decodeURIComponent(atob(str).replace(/(.)/g, function (m, p) {
                var code = p.charCodeAt(0).toString(16).toUpperCase();
                if (code.length < 2) {
                    code = '0' + code;
                }
                return '%' + code;
            }));
        }

        function decode(str) {
            var output = str.replace(/-/g, "+").replace(/_/g, "/");
            switch (output.length % 4) {
                case 0:
                    break;
                case 2:
                    output += "==";
                    break;
                case 3:
                    output += "=";
                    break;
                default:
                    throw "Illegal base64url string!";
            }

            try {
                return b64DecodeUnicode(output);
            } catch (err) {
                return atob(output);
            }
        }

        var jwtArray = jwt.split('.');

        return {
            header: decode(jwtArray[0]),
            payload: decode(jwtArray[1]),
            signature: decode(jwtArray[2])
        };

    };

D
Derek Soike

用于解码 JSON Web 令牌 (JWT) 的简单 NodeJS 解决方案

function decodeTokenComponent(value) {
    const buff = new Buffer(value, 'base64')
    const text = buff.toString('ascii')
    return JSON.parse(text)
}

const token = 'xxxxxxxxx.XXXXXXXX.xxxxxxxx'
const [headerEncoded, payloadEncoded, signature] = token.split('.')
const [header, payload] = [headerEncoded, payloadEncoded].map(decodeTokenComponent)

console.log(`header: ${header}`)
console.log(`payload: ${payload}`)
console.log(`signature: ${signature}`)

M
Matthieu Bosquet

在 Node.js (TypeScript) 中:

import { TextDecoder } from 'util';

function decode(jwt: string) {
    const { 0: encodedHeader, 1: encodedPayload, 2: signature, length } = jwt.split('.');

    if (length !== 3) {
        throw new TypeError('Invalid JWT');
    }

    const decode = (input: string): JSON => { return JSON.parse(new TextDecoder().decode(new Uint8Array(Buffer.from(input, 'base64')))); };

    return { header: decode(encodedHeader), payload: decode(encodedPayload), signature: signature };
}

使用 jose by panva on GitHub,您可以使用最小的 import { decode as base64Decode } from 'jose/util/base64url' 并将 new Uint8Array(Buffer.from(input, 'base64')) 替换为 base64Decode(input)。然后代码应该可以在浏览器和 Node.js 中运行。


a
agm1984

这是我在研究此问题后刚刚提出的功能更丰富的解决方案:

const parseJwt = (token) => {
    try {
        if (!token) {
            throw new Error('parseJwt# Token is required.');
        }

        const base64Payload = token.split('.')[1];
        let payload = new Uint8Array();

        try {
            payload = Buffer.from(base64Payload, 'base64');
        } catch (err) {
            throw new Error(`parseJwt# Malformed token: ${err}`);
        }

        return {
            decodedToken: JSON.parse(payload),
        };
    } catch (err) {
        console.log(`Bonus logging: ${err}`);

        return {
            error: 'Unable to decode token.',
        };
    }
};

以下是一些使用示例:

const unhappy_path1 = parseJwt('sk4u7vgbis4ewku7gvtybrose4ui7gvtmalformedtoken');
console.log('unhappy_path1', unhappy_path1);

const unhappy_path2 = parseJwt('sk4u7vgbis4ewku7gvtybrose4ui7gvt.malformedtoken');
console.log('unhappy_path2', unhappy_path2);

const unhappy_path3 = parseJwt();
console.log('unhappy_path3', unhappy_path3);

const { error, decodedToken } = parseJwt('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c');
if (!decodedToken.exp) {
    console.log('almost_happy_path: token has illegal claims (missing expires_at timestamp)', decodedToken);
    // note: exp, iat, iss, jti, nbf, prv, sub
}

我无法在 StackOverflow 代码片段工具中使其可运行,但如果您运行该代码,您会看到以下内容:

https://i.stack.imgur.com/FdLZU.png

我让 parseJwt 函数总是返回一个对象(在某种程度上是出于静态类型的原因)。

这允许您使用以下语法:

const { decodedToken, error } = parseJwt(token);

然后,您可以在运行时测试特定类型的错误并避免任何命名冲突。

如果有人能想到对此代码进行任何低努力、高价值的更改,请随时编辑我的答案以受益于 next(person)


w
webjay

根据此处的答案和here

const dashRE = /-/g;
const lodashRE = /_/g;

module.exports = function jwtDecode(tokenStr) {
  const base64Url = tokenStr.split('.')[1];
  if (base64Url === undefined) return null;
  const base64 = base64Url.replace(dashRE, '+').replace(lodashRE, '/');
  const jsonStr = Buffer.from(base64, 'base64').toString();
  return JSON.parse(jsonStr);
};

M
Morgan

jwt-decode.js 的 es 模块友好简化版本

function b64DecodeUnicode(str) {
  return decodeURIComponent(
    atob(str).replace(/(.)/g, function (m, p) {
      var code = p.charCodeAt(0).toString(16).toUpperCase();
      if (code.length < 2) {
        code = "0" + code;
      }
      return "%" + code;
    })
  );
}

function base64_url_decode(str) {
  var output = str.replace(/-/g, "+").replace(/_/g, "/");
  switch (output.length % 4) {
    case 0:
      break;
    case 2:
      output += "==";
      break;
    case 3:
      output += "=";
      break;
    default:
      throw "Illegal base64url string!";
  }

  try {
    return b64DecodeUnicode(output);
  } catch (err) {
    return atob(output);
  }
}

export function jwtDecode(token, options) {
  options = options || {};
  var pos = options.header === true ? 0 : 1;
  try {
    return JSON.parse(base64_url_decode(token.split(".")[pos]));
  } catch (e) {
    console.log(e.message);
  }
}

那个包 ES Module 不是很友好吗?
为什么需要将“-”替换为“+”,将“_”替换为“/”?