Làm cách nào để giải mã mã thông báo jwt trong javascript mà không cần sử dụng thư viện?


208

Làm cách nào để giải mã tải trọng của JWT bằng JavaScript? Không có thư viện. Vì vậy, mã thông báo chỉ trả về một đối tượng tải trọng có thể được sử dụng bởi ứng dụng giao diện người dùng của tôi.

Mã thông báo ví dụ: xxxxxxxxx.XXXXXXXX.xxxxxxxx

Và kết quả là tải trọng:

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

1
Nó được mã hóa như thế nào? Chỉ cần làm ngược lại. Bạn sẽ cần bí mật được chia sẻ.
Lucky Soni

Nó được mã hóa bởi api phụ trợ sử dụng thư viện php. Ở đây tôi cần là tải trọng được mã hóa bằng base64 tôi đoán ...
Chrisk8er

1
Bạn có thể thử truy cập trang web jwt.io và nhận thư viện JavaScript mà nó cung cấp.
Quentin

12
Vì câu hỏi này có một số lưu lượng truy cập, tôi muốn thêm từ chối trách nhiệm: Nếu bạn giải mã một cách mù quáng tải trọng của mã thông báo, mà không xác thực chữ ký, bạn có thể (hoặc không) gặp phải các vấn đề bảo mật! Hãy chắc chắn rằng bạn hiểu kiến ​​trúc bảo mật của mình, trước khi sử dụng bất kỳ mã nào được cung cấp trong câu hỏi stackoverflow này.
Carsten Hoffmann

4
@CarstenHoffmann Và chính xác làm thế nào để tôi xác nhận chữ ký ??
Saurabh Tiwari

Câu trả lời:


467

Hoạt động chức năng trình phân tích cú pháp văn bản 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
Thật không may, điều này dường như không hoạt động với văn bản unicode.
Paul McMahon

2
Giải pháp này thậm chí có thể được sử dụng trong Postman (tap thử nghiệm) vì nó không yêu cầu cài đặt thư viện bổ sung. Tôi đã sử dụng nó để trích xuất userid từ mã xác thực.
Wlad

2
LƯU Ý: Trong Postman, tôi phải xóa "cửa sổ" JSON.parse(window.atob(base64))để làm cho nó hoạt động. Chỉ return JSON.parse(atob(base64));và sau đó postman.setEnvironmentVariable("userId", parseJwt(jsonData.access_token)); "access_token" là trong trường hợp của tôi, khóa của giá trị mã thông báo trong phản hồi (có thể khác trong trường hợp của bạn).
Wlad

12
Giải pháp trên chỉ thay thế "-" và "_" đầu tiên trong mã thông báo (một tính năng "javascript" khiến tôi đau đớn). Chỉ cần thay thế dòng thứ ba trong câu trả lời bằng:var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
Racing Tadpole

2
Tốt hơn là sử dụng jwt-decodemô-đun vì nó nhỏ nhưng xử lý tốt hơn một chút.
Rantiev


47

Bạn có thể sử dụng jwt-decode , sau đó bạn có thể viết:

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

65
"Ý tôi là không có thư viện."
SherloxTV

Họ đang gặp vấn đề với thư viện này. Chủ yếu với firefox sử dụng. Vấn đề mà tôi gặp phải là nếu mã thông báo == null xuất phát từ việc đăng xuất hoặc hết hạn; rằng điều này chỉ giết chết trang.
LUser

1
@ApertureSecurance bạn cần phải bắt lỗi này, nhưng phải thừa nhận rằng đây là lý do tại sao tôi không muốn sử dụng thư viện này
Luke Robertson

Điều này dường như không hỗ trợ GZIP. Trên thực tế, tôi không thể tìm thấy bất kỳ thư viện JS nào hỗ trợ GZIP cho các khiếu nại.
Andrew T Finnell

18

bạn có thể sử dụng atob()chức năng javascript thuần túy để giải mã mã thông báo thành một chuỗi:

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

hoặc phân tích trực tiếp nó thành một đối tượng json:

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

đọc atob()và tích btoa()hợp các chức năng javascript Mã hóa và giải mã Base64 - API Web | MDN .


9

@Peheje sẽ hoạt động, nhưng bạn sẽ gặp vấn đề với unicode. Để sửa lỗi, tôi sử dụng mã trên 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 nhưng nếu nhận xét của Racing Tadpole về câu trả lời của Peheje là chính xác (rằng các cuộc gọi thay thế sẽ chỉ thay thế phiên bản đầu tiên), thì cách khắc phục tương tự sẽ được áp dụng ở đây.
Gary McGill

9

Vì đối tượng "window" không có trong môi trường nodejs, chúng ta có thể sử dụng các dòng mã sau:

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

Nó làm việc cho tôi hoàn hảo. Hy vọng nó giúp.


1
câu trả lời hoàn hảo cho nút 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);

Nếu sử dụng nút, bạn có thể phải sử dụng gói bộ đệm:

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

6

Tôi sử dụng chức năng này để nhận tải trọng, tiêu đề, exp (Thời gian hết hạn), iat (Đã phát hành) dựa trên câu trả lời này

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

Câu trả lời này có phần tốt hơn, nhưng nó có hai vấn đề rưỡi. Đầu tiên, nó không kiểm tra chữ ký (mảng 2). Thứ hai, REPLACE sẽ không hoạt động chính xác, vì họ bỏ lỡ cờ "g" trên regex (sẽ chỉ thay thế các lần xuất hiện đầu tiên của - và _ trên JWT, như Racing Tadpole đã nhận xét trên một bài đăng khác). Và một nửa: để giải mã các mục mảng 0 và 1, bạn có thể đã sử dụng vòng lặp FOR, thay vì sao chép toàn bộ mã (đó là một mã ngắn, nhưng có thể được thực hiện hiệu quả hơn, như cách thức, SPLIT được thực thi hai lần ).
Cyberknight

4

tất cả các tính năng của jwt.io không hỗ trợ tất cả các ngôn ngữ. Trong NodeJs bạn có thể sử dụng

var decoded = jwt.decode(token);

1
Không có thư viện, bạn chỉ thực hiện giải mã base64 trong phần thứ hai của mã thông báo {var payload = token.split ('.') [1]); } Sau đó thực hiện giải mã base64 {var decodingData = atob (payload); }
Jithin Vijayan

4

Tôi tìm thấy mã này tại jwt.io và nó hoạt động tốt.

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

Trong một số trường hợp (nền tảng phát triển nhất định),
câu trả lời tốt nhất (hiện tại) phải đối mặt với vấn đề về độ dài cơ sở64 không hợp lệ.
Vì vậy, tôi cần một cách ổn định hơn.

Tôi hy vọng nó sẽ giúp bạn.


2

Cả Guy và Peheje đều đã trả lời câu hỏi. Đối với một người mới bắt đầu như tôi, thật hữu ích khi có dòng nhập được xác định trong ví dụ.

Ngoài ra, tôi đã mất vài phút để nhận ra rằng mã thông báo là tập hợp đầy đủ các thông tin đăng nhập được gửi lại (toàn bộ mã thông báo JWT, không chỉ là phần idToken của nó). Nói thẳng một khi bạn biết điều đó ..

import jwt_decode from 'jwt-decode';

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

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


2
Đăng câu trả lời chính xác giống như một người dùng khác cũng đi ngược lại những gì OP yêu cầu không hữu ích lắm
Cacoon

2

Giải pháp NodeJS đơn giản để giải mã mã thông báo web 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

Trả lời dựa trên GitHub - auth0 / jwt-decode . Đã thay đổi đầu vào / đầu ra để bao gồm tách chuỗi và trả về đối tượng {tiêu đề, tải trọng, chữ ký} để bạn có thể chuyển toàn bộ mã thông báo.

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

Đây là một giải pháp giàu tính năng hơn mà tôi vừa thực hiện sau khi nghiên cứu câu hỏi này:

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.',
        };
    }
};

Dưới đây là một số mẫu sử dụng:

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
}

Tôi không thể làm cho nó có thể chạy được trong công cụ đoạn mã StackOverflow, nhưng đây là khoảng những gì bạn sẽ thấy nếu bạn chạy mã đó:

nhập mô tả hình ảnh ở đây

Tôi đã làm cho parseJwthàm luôn trả về một đối tượng (ở một mức độ nào đó vì lý do gõ tĩnh).

Điều này cho phép bạn sử dụng cú pháp như:

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

Sau đó, bạn có thể kiểm tra tại thời điểm chạy cho các loại lỗi cụ thể và tránh mọi xung đột đặt tên.

Nếu bất cứ ai có thể nghĩ về bất kỳ nỗ lực thấp, giá trị cao nào thay đổi mã này, vui lòng chỉnh sửa câu trả lời của tôi vì lợi ích của next(person).


0

Dựa trên câu trả lời ở đây và đây :

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

Chạy Javascript node.js express trước tiên tôi phải cài đặt gói như sau:

npm install jwt-decode --save

sau đó trong mã app.js của tôi lấy gói:

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

Sau đó chạy mã:

let jwt_decoded = jwt_decode(jwt_source);

Rồi phép màu:

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

4
nhớ "không sử dụng thư viện"
Olaf

1
được rồi. Tuy nhiên, tôi đã phải đối mặt với cùng một vấn đề và tôi không bị hạn chế không thể sử dụng thư viện. Điều này làm việc cho tôi. Tôi để nó được đăng vì có thể người khác phải đối mặt với một vấn đề tương tự và không có hạn chế tương tự.
David White
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.