วิธีการถอดรหัส jwt token ใน 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 ที่มีให้
Quentin

12
เนื่องจากคำถามนี้มีทราฟฟิกจำนวนหนึ่งฉันต้องการเพิ่มข้อจำกัดความรับผิดชอบ: หากคุณถอดรหัส payload ของโทเค็นโดยไม่ต้องตรวจสอบลายเซ็นคุณอาจ (หรืออาจไม่) รับปัญหาด้านความปลอดภัย! ตรวจสอบให้แน่ใจว่าคุณเข้าใจสถาปัตยกรรมความปลอดภัยของคุณก่อนที่จะใช้รหัสใด ๆ
Carsten Hoffmann

4
@CarstenHoffmann และฉันจะตรวจสอบลายเซ็นได้อย่างไร?
Saurabh Tiwari

คำตอบ:


468

การทำงาน unicode text jWT parser function

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
พอลแมคมาฮอน

2
วิธีการแก้ปัญหานี้ยังสามารถใช้ในบุรุษไปรษณีย์ (แตะการทดสอบ) เพราะมันไม่จำเป็นต้องมีการติดตั้งห้องสมุดนอกจากนี้ ฉันใช้มันเพื่อดึงหมายเลขผู้ใช้จาก auth-token
ลาด

2
หมายเหตุ: ในบุรุษไปรษณีย์ฉันต้องลบ "หน้าต่าง" ออกจากJSON.parse(window.atob(base64))เพื่อให้ทำงานได้ เพียงแค่return JSON.parse(atob(base64));นั้นpostman.setEnvironmentVariable("userId", parseJwt(jsonData.access_token)); "access_token" ในกรณีของฉันคีย์ของค่าโทเค็นในการตอบสนอง (อาจแตกต่างกันในกรณีของคุณ)
ลาด

12
วิธีการแก้ปัญหาข้างต้นจะแทนที่ "-" และ "_" ตัวแรกในโทเค็น (คุณลักษณะจาวาสคริปต์ "" ที่ทำให้ฉันเจ็บปวด) เพียงแค่แทนที่บรรทัดที่สามในคำตอบด้วย:var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
Racing Tadpole

2
มันจะดีกว่าที่จะใช้jwt-decodeโมดูลเพราะมันมีขนาดเล็ก แต่ใช้งานได้ดีขึ้นเล็กน้อย
Rantiev


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

67
"ฉันหมายถึงไม่มีห้องสมุด"
SherloxTV

พวกเขามีปัญหากับห้องสมุดนี้ ส่วนใหญ่กับ firefox ที่ใช้งานอยู่ ปัญหาที่ฉันพบคือว่าถ้าโทเค็น == null เกิดจากการออกจากระบบหรือหมดอายุ ว่านี่เป็นเพียงการฆ่าหน้าด้วยข้อผิดพลาด
LUser

1
@ ApertureSecurity คุณต้องจับข้อผิดพลาดนี้ แต่ก็ยอมรับว่านี่คือเหตุผลที่ฉันไม่ต้องการใช้ห้องสมุดนี้
Luke Robertson

ดูเหมือนจะไม่รองรับ GZIP ที่จริงแล้วฉันไม่สามารถหาไลบรารี JS ที่สนับสนุน GZIP สำหรับการอ้างสิทธิ์ได้
Andrew T Finnell

18

คุณสามารถใช้atob()ฟังก์ชั่นจาวาสคริปต์บริสุทธิ์เพื่อถอดรหัสโทเค็นลงในสตริง:

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

หรือแยกโดยตรงลงในวัตถุ json:

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

อ่านเกี่ยวกับatob()และbtoa()ฟังก์ชั่นจาวาสคริปต์Base64 เข้ารหัสและถอดรหัส - Web APIs | MDN


9

@ Peheje จะทำงานได้ แต่คุณจะมีปัญหากับยูนิโค้ด ที่จะแก้ไขได้ผมใช้รหัสใน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

เนื่องจากวัตถุ "window" ไม่มีอยู่ในสภาพแวดล้อมของ nodejs เราสามารถใช้บรรทัดของรหัสต่อไปนี้:

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) ประการที่สอง REPLACEs ทำงานไม่ถูกต้องเพราะพวกเขาพลาดการตั้งค่าสถานะ "g" ใน regex (จะแทนที่เหตุการณ์แรกของ - และ _ ใน JWT เท่านั้นเช่น Racing Tadpole แสดงความคิดเห็นในโพสต์อื่น) และครึ่งหนึ่ง: ในการถอดรหัสรายการอาร์เรย์ 0 และ 1 คุณสามารถใช้วนวน FOR แทนการทำซ้ำรหัสทั้งหมด (เป็นรหัสย่อ แต่อาจทำให้มีประสิทธิภาพมากขึ้นตามที่เป็นอยู่ SPLIT จะถูกดำเนินการสองครั้ง )
Cyberknight

4

คุณสมบัติทั้งหมดของ jwt.io ไม่รองรับทุกภาษา ใน NodeJs คุณสามารถใช้

var decoded = jwt.decode(token);

1
หากไม่มีไลบรารีคุณเพียงทำการถอดรหัส base64 ในส่วนที่สองของโทเค็น {var payload = token.split ('.') [1]); } จากนั้นทำการถอดรหัส base64 {var decodedData = 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;
  }
}

ในบางกรณี (แพลตฟอร์มการพัฒนาบางอย่าง)
คำตอบที่ดีที่สุด (ตอนนี้)ประสบปัญหาความยาวฐานที่ไม่ถูกต้อง
ดังนั้นฉันต้องการวิธีที่มีเสถียรภาพมากขึ้น

ฉันหวังว่ามันจะช่วยคุณ


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

Simple NodeJS Solution สำหรับการถอดรหัส JSON Web Token (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 แก้ไขอินพุต / เอาต์พุตเพื่อรวมการแยกสตริงและส่งคืนอ็อบเจ็กต์ {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])
        };

    };

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
}

ฉันไม่สามารถทำให้ runnable นั้นในเครื่องมือข้อมูลโค้ด 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

จากนั้นในรหัสแอพของฉันรับแพ็คเกจ:

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

จากนั้นเรียกใช้รหัส:

let jwt_decoded = jwt_decode(jwt_source);

จากนั้นเวทมนตร์:

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

4
จำไว้ว่า "โดยไม่ต้องใช้ห้องสมุด"
Olaf

1
โอเคพอยุติธรรม อย่างไรก็ตามฉันประสบปัญหาเดียวกันและฉันไม่ได้มีข้อ จำกัด ที่ไม่สามารถใช้ห้องสมุดได้ สิ่งนี้ใช้ได้สำหรับฉัน ฉันปล่อยให้มันโพสต์เพราะบางทีคนอื่นประสบปัญหาที่คล้ายกันและไม่มีข้อ จำกัด เหมือนกัน
David White
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.