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


208

如何使用JavaScript解码JWT的有效负载?没有图书馆。因此,令牌仅返回可由我的前端应用程序使用的有效负载对象。

令牌示例: xxxxxxxxx.XXXXXXXX.xxxxxxxx

结果是有效负载:

{exp: 10012016 name: john doe, scope:['admin']}

1
它是如何编码的?只是相反。您将需要共享机密。
Lucky Soni

它由使用php库的后端api编码。在这里,我需要的是我猜想使用base64编码的有效载荷……
Chrisk8er

1
您可以尝试访问jwt.io网站并获取它提供的JavaScript库。
昆汀

12
由于这个问题会带来一些麻烦,因此我想添加一个免责声明:如果您盲目地解码令牌的有效负载而未验证签名,则可能(或可能不会)遇到安全问题!在盲目使用此stackoverflow问题中提供的任何代码之前,请确保您了解安全体系结构。
卡斯滕·霍夫曼

4
@CarstenHoffmann以及我究竟如何验证签名?
萨拉巴·蒂瓦里

Answers:


468

起作用的Unicode文本JWT解析器功能:

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

    return JSON.parse(jsonPayload);
};

2
不幸的是,这似乎不适用于unicode文本。
Paul McMahon

2
该解决方案甚至可以在Postman中使用(测试水龙头),因为它不需要安装任何附加库。我用它从auth令牌中提取userid。
Wlad

2
注意:在邮递员中,我必须删除“窗口” JSON.parse(window.atob(base64))以使其起作用。只需return JSON.parse(atob(base64));postman.setEnvironmentVariable("userId", parseJwt(jsonData.access_token)); “ACCESS_TOKEN”是我的情况下,代币价值的响应(在你的情况可能有所不同)的关键。
Wlad

12
上面的解决方案仅替换了令牌中的第一个“-”和“ _”(不断使我感到痛苦的javascript“功能”)。只需将答案中的第三行替换为:var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
赛车Ta

2
最好使用jwt-decode模块,因为它很小,但处理起来更好。
拉蒂涅夫


47

您可以使用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']}*/

66
“我的意思是没有图书馆。”
SherloxTV

他们是这个图书馆的问题。主要与firefox一起使用。我遇到的问题是,如果由于注销或过期而导致令牌== null;这只会杀死页面并出现错误。
LUser

1
@ApertureSecurity,您需要捕获此错误,但是,坦率地说,这就是为什么我不想使用此库的原因
Luke Robertson

这似乎不支持GZIP。实际上,我找不到任何支持GZIP声明的JS库。
Andrew T Finnell '18


9

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

let b64DecodeUnicode = str =>
  decodeURIComponent(
    Array.prototype.map.call(atob(str), c =>
      '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
    ).join(''))

let parseJwt = token =>
  JSON.parse(
    b64DecodeUnicode(
      token.split('.')[1].replace('-', '+').replace('_', '/')
    )
  )


let form = document.getElementById("form")
form.addEventListener("submit", (e) => {
   form.out.value = JSON.stringify(
      parseJwt(form.jwt.value)
   )
   e.preventDefault();
})
textarea{width:300px; height:60px; display:block}
<form id="form" action="parse">
  <textarea name="jwt">eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkrDtGhuIETDs8OoIiwiYWRtaW4iOnRydWV9.469tBeJmYLERjlKi9u6gylb-2NsjHLC_6kZNdtoOGsA</textarea>
  <textarea name="out"></textarea>
  <input type="submit" value="parse" />
</form>


+1,但如果Racing Tadpole对Peheje的回答的评论是正确的(替换调用将仅替换第一个实例),则此处将应用相同的修复方法。
Gary McGill

9

由于在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'));

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


1
Node js的完美答案
ireshan pathirana

7
function parseJwt(token) {
  var base64Payload = token.split('.')[1];
  var payload = Buffer.from(base64Payload, 'base64');
  return JSON.parse(payload);
}
let payload= parseJwt("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c");
console.log("payload:- ", payload);

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

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

6

我根据答案使用此功能来获取有效载荷,标头,exp(到期时间),iat(发行日期)

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执行两次)。
Cyber​​knight

4

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

var decoded = jwt.decode(token);

1
没有库,您只需在令牌的第二部分执行BASE64解码{var payload = token.split('。')[1]); }然后执行base64解码{var encodedData = atob(payload); }
Jithin Vijayan

4

我在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;
  }
}

在某些情况下(某些开发平台),
最好的答案(目前)面临无效的base64长度的问题。
因此,我需要一种更稳定的方法。

希望对您有帮助。


2

Guy和Peheje都已经回答了这个问题。对于像我这样的新手来说,在示例中定义导入行也很有帮助。

另外,我花了几分钟的时间才弄清楚令牌是要回发的全套凭据(整个JWT令牌,而不仅仅是IDToken的一部分)。一经了解就直截了当..

import jwt_decode from 'jwt-decode';

var token = 'eyJ0eXAiO.../// jwt token';
var decoded = jwt_decode(token);

/*{exp: 10012016 name: john doe, scope:['admin']}*/


2
发布与另一个用户完全相同的答案(也违反OP的要求)不是很有帮助
Cacoon

2

用于解码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}`)

2

来自GitHub的答案-auth0 / jwt-decode。更改了输入/输出,以包括字符串拆分和返回对象{标头,有效负载,签名},因此您只需传递整个令牌即可。

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])
        };

    };

1

这是我研究了这个问题后才提出的功能更丰富的解决方案:

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代码段工具中将其设为可运行状态,但是如果您运行该代码,则大致可以看到以下内容:

在此处输入图片说明

我使parseJwt函数总是返回一个对象(出于某种原因,出于静态类型的考虑)。

这使您可以利用以下语法:

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

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

如果有人可以想到此代码的任何省力,高价值的更改,请随时为受益而编辑我的答案next(person)


0

根据此处和此处的答案:

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);
};

-1

运行Javascript node.js express,我必须首先按照以下步骤安装软件包:

npm install jwt-decode --save

然后在我的app.js代码中获取软件包:

const jwt_decode = require('jwt-decode');

然后运行代码:

let jwt_decoded = jwt_decode(jwt_source);

然后魔术:

console.log('sub:',jwt_decoded.sub);

4
记住“不使用图书馆”
Olaf

1
好,可以。但是,我面临着同样的问题,而且我没有无法使用库的限制。这对我有用。我将其发布,因为其他人可能面临类似的问题并且没有相同的限制。
大卫·怀特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.