Як розшифрувати jwt маркер у javascript без використання бібліотеки?


209

Як я можу розшифрувати корисне навантаження JWT за допомогою JavaScript? Без бібліотеки. Тож маркер просто повертає об'єкт корисного навантаження, який може споживати мій передній додаток.

Приклад маркера: xxxxxxxxx.XXXXXXXX.xxxxxxxx

І результат - корисне навантаження:

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

1
Як це було закодовано? Просто робіть зворотне. Вам знадобиться спільний секрет.
Lucky Soni

Він був закодований бекендом api, який використовував php-бібліотеку. Тут мені потрібна корисна навантаження, кодована за допомогою base64, я здогадуюсь ...
Chrisk8er

1
Ви можете спробувати перейти на веб-сайт jwt.io та отримати бібліотеку JavaScript, яку він надає.
Квентін

12
Оскільки це запитання має певний трафік, я хочу додати відмову: Якщо ви сліпо декодуєте корисне навантаження маркера, не підтверджуючи підпис, ви можете (або не можете) зіткнутися з проблемами безпеки! Переконайтесь, що ви розумієте свою архітектуру безпеки, перш ніж сліпо користуватися будь-яким кодом, наданим у цьому питанні про stackoverflow.
Карстен Гофман

4
@CarstenHoffmann А як саме я підтверджую підпис ??
Саурабх Тіварі

Відповіді:


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
На жаль, це, здається, не працює з текстом унікоду.
Пол Макмахон

2
Це рішення можна використовувати навіть у програмі Postman (тест-кран), оскільки воно не потребує встановлення додаткової бібліотеки. Я використовував його для вилучення userid з auth-маркера.
Вів

2
ПРИМІТКА: У листоноші мені довелося видалити "вікно", JSON.parse(window.atob(base64))щоб воно працювало. Якраз return JSON.parse(atob(base64));і тоді postman.setEnvironmentVariable("userId", parseJwt(jsonData.access_token)); "access_token" - це в моєму випадку ключ значення маркера у відповідь (може відрізнятися у вашому випадку).
Одяг

12
Вищевказане рішення замінює лише перші "-" та "_" в маркері ("функція javascript", яка постійно завдає мені болю). Просто замініть третій рядок у відповіді на:var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
Racing Tadpole

2
Краще використовувати jwt-decodeмодуль, тому що він невеликий, але трохи краще.
Рантьєв


47

Ви можете використовувати jwt-декодування , щоб потім ви могли написати:

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']}*/

67
"Я маю на увазі відсутність бібліотеки".
SherloxTV

У них проблеми з цією бібліотекою. В основному з firefox у використанні. Проблема, з якою я зіткнувся, полягала в тому, що якщо маркер == null в результаті виходу з системи або закінчення терміну дії; що це просто вбиває сторінку з помилкою.
ЛЮсер

1
@ApertureSecurity вам потрібно зрозуміти цю помилку, але, правда, саме тому я не хочу використовувати цю бібліотеку
Люк Робертсон

Схоже, це не підтримує GZIP. Насправді я не можу знайти жодної бібліотеки JS, яка підтримує GZIP для претензій.
Ендрю Т Фіннелл

18

ви можете використовувати чисту atob()функцію javascript, щоб декодувати маркер у рядок:

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

або проаналізувати його безпосередньо в об'єкт json:

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

читати про atob()та btoa()вбудованої в JavaScript функції Base64 кодування і декодування - веб - АФІ | MDN .


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 правильний (що виклики на заміну замінять лише перший екземпляр), то тут буде застосовано те саме виправлення.
Гері Макгілл

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
ідеальна відповідь для вузла 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 масиву). По-друге, ЗАМІНИ не працюватимуть правильно, оскільки вони пропускають прапорець "g" на регулярному виразі (замінить лише перші випадки - і _ на JWT, як Racing Tadpole прокоментував інший пост). І половина: для декодування елементів масиву 0 і 1 ви могли використовувати цикл FOR замість дублювання всього коду (це короткий код, але його можна зробити більш ефективним, оскільки, як це відбувається, SPLIT виконується двічі ).
Кіберночі

4

всі функції jwt.io підтримують не всі мови. У NodeJs ви можете використовувати

var decoded = jwt.decode(token);

1
Без бібліотеки ви просто виконуєте декодування base64 у другій частині токена {var payload = token.split ('.') [1]); } Потім виконайте декодування base64 {var decodedData = atob (корисне навантаження); }
Джітін Віджаян

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

І Гай, і Пехей вже відповіли на питання. У цілому для початківців, як я, було б корисно також вказати рядок імпорту у прикладі.

Також мені знадобилося кілька хвилин, щоб зрозуміти, що маркер - це повний набір облікових даних, який надсилається назад (весь 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
Опублікувати таку саму відповідь, як і інший користувач, яка також суперечить тому, про що просили ОП, не дуже корисно
Cacoon

2

Просте рішення NodeJS для декодування веб-маркера JSON (JWT)

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-декодування . Змінено вхід / вихід, щоб включити об'єкт розбиття рядків і повернення об'єкта {заголовок, корисний вантаж, підпис}, щоб ви могли просто передати весь маркер.

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
пам’ятайте «без використання бібліотеки»
Олаф

1
добре справедливий досить. Однак я зіткнувся з тією ж проблемою, і у мене не було обмеження неможливості користуватися бібліотекою. Це працювало для мене. Я залишаю це повідомлення, як, можливо, хтось інший стикається з подібною проблемою і не має таких самих обмежень.
Девід Уайт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.