简体   繁体   English

在 ember-cli-mirage(或任何 JavaScript)中生成 jwt web auth 令牌以在您的 ember 应用程序中使用

[英]Generate jwt web auth tokens in ember-cli-mirage (or any JavaScript) for use in your ember app

I work on an Ember team that implemented djangorestframework-simplejwt for our API security.我在 Ember 团队工作,该团队为我们的 API 安全性实施了 djangorestframework-simplejwt。 It's a good API solution, but our mirage user was getting logged out after a period of time and could not log back into our app (for testing, development).这是一个很好的 API 解决方案,但是我们的 Mirage 用户在一段时间后被注销并且无法重新登录我们的应用程序(用于测试、开发)。 I traced the problem down to how jwt works, and the fact that I had pasted static jwt tokens in our mirage config /login endpoint.我将问题追溯到 jwt 的工作原理,以及我在 Mirage 配置 /login 端点中粘贴静态 jwt 令牌的事实。

jwt or JSON Web Tokens contain an expiration date, set on the server. jwt 或 JSON Web 令牌包含在服务器上设置的到期日期。 Once that expiration date passes, the client cannot be auth'ed into the app anymore, until the server sends a new token with a future expiration date.一旦过期日期过去,客户端将无法再通过身份验证进入应用程序,直到服务器发送具有未来过期日期的新令牌。 This was a problem for our mirage ENV, because the mirage endpoint for /login was returning a static jwt token which I had copy/pasted from our backend response.这对我们的 mirage ENV 来说是一个问题,因为 /login 的 mirage 端点返回了一个静态 jwt 令牌,我从我们的后端响应中复制/粘贴了该令牌。 The workaround was to get new tokens from our backend, paste them into our mirage config and use them until they expire, which is not a true permanent solution to this problem.解决方法是从我们的后端获取新令牌,将它们粘贴到我们的 Mirage 配置中并使用它们直到它们过期,这不是解决此问题的真正永久解决方案。

After a LOT of trial and error (and learning way too much about jwt), I came up with this solution, which creates a valid jwt token with an expiration date 7 days in the future.经过大量的反复试验(以及对 jwt 的了解太多),我想出了这个解决方案,它创建了一个有效的 jwt 令牌,有效期为未来 7 天。 It only requires crypto-js (npm install crypto-js), a very lightweight library with many crypto functions, but no dependencies:它只需要crypto-js (npm install crypto-js),一个非常轻量级的库,有很多加密函数,但没有依赖:

import CryptoJS from 'CryptoJS';

const generateTokens = function(secretKey) { // fn to generate jwt access and refresh tokens with live date (+7 days) expiration
  let newEpochDate = new Date().valueOf();
  newEpochDate += 6.048e8; // add 7 days padding
  newEpochDate = Math.trunc(newEpochDate / 1000); // convert to Java epoch date value
  let tokenObjBase = {
    'typ': 'JWT',
    'alg': 'HS256'
  };
  let tokenObjAccess = {
    'token_type': 'access',
    'exp': newEpochDate,
    'jti': '83bc20a2fb564aa8937d167586166f67',
    'user_id': 24865
  };
  let tokenObjRefresh = {
    'token_type': 'refresh',
    'exp': newEpochDate,
    'jti': '83bc20a2fb564aa8937d167586166f67',
    'user_id': 24865
  };

  let base64urlEncode = function (obj) {
    let base64url = CryptoJS.enc.Utf8.parse(JSON.stringify(obj)).toString(CryptoJS.enc.Base64);
    base64url = base64url.replace(/=/g, '').replace(/\//g, '_').replace(/\+/g, '-'); // crypto-js doesn't have base64url encoding; we must manually make the tokens URL safe
    return base64url;
  }
  let tokenBase = base64urlEncode(tokenObjBase);
  let tokenAccess = base64urlEncode(tokenObjAccess);
  let tokenRefresh = base64urlEncode(tokenObjRefresh);

  let signatureAccessArray = CryptoJS.HmacSHA256(tokenBase + '.' + tokenAccess, secretKey); // crypto-js returns a "wordarray" which must be stringified back to human readable text with a specific encoding
  let signatureAccess = signatureAccessArray.toString(CryptoJS.enc.Base64).replace(/=+$/, '').replace(/\//g, '_').replace(/\+/g, '-'); // crypto-js doesn't have base64url encoding; we must manually make the tokens URL safe
  let signatureRefreshArray = CryptoJS.HmacSHA256(tokenBase + '.' + tokenRefresh, secretKey);
  let signatureRefresh = signatureRefreshArray.toString(CryptoJS.enc.Base64).replace(/=+$/, '').replace(/\//g, '_').replace(/\+/g, '-'); // crypto-js doesn't have base64url encoding; we must manually make the tokens URL safe

  return {tokenRefresh: tokenBase + '.' + tokenRefresh + '.' + signatureRefresh, tokenAccess: tokenBase + '.' + tokenAccess + '.' + signatureAccess};
}

export default function() { // ...rest of mirage/config.js

// you may also need this in your ember-cli-build:
app.import('node_modules/crypto-js/crypto-js.js', {
  using: [
    { transformation: 'amd', as: 'CryptoJS' }
  ]
});

This fn can be called by any route in the config file: let tokens = generateTokens('thisisnotarealsecretkey');这个 fn 可以被配置文件中的任何路由调用: let tokens = generateTokens('thisisnotarealsecretkey');

It returns an object with an "access" token and a "refresh" token, the two token types required by our django jwt setup.它返回一个带有“访问”令牌和“刷新”令牌的对象,这是我们的 django jwt 设置所需的两种令牌类型。 Customize the tokenObjBase, tokenObjAccess and tokenObjRefresh to meet your backend's setup.自定义 tokenObjBase、tokenObjAccess 和 tokenObjRefresh 以满足您的后端设置。

The basic structure of a jwt token can be found here: https://jwt.io/ jwt 令牌的基本结构可以在这里找到: https : //jwt.io/

To summarize, a jwt token has three strings, separated by two periods (.).总而言之,jwt 令牌具有三个字符串,由两个句点 (.) 分隔。

The first string is the tokenObjBase passed through JSON.stringify(), then converted to a base64URL value.第一个字符串是通过 JSON.stringify() 传递的 tokenObjBase,然后转换为 base64URL 值。 That URL part is important, because regular base64 encodings don't remove the =, + and / chars, which are not "web safe." URL部分很重要,因为常规 base64 编码不会删除 =、+ 和 / 字符,它们不是“网络安全”。 The tokenObjBase must contain typ and alg properties and nothing else. tokenObjBase 必须包含typalg属性,没有别的。

The second string is your "payload" (here, tokenObjAccess or tokenObjRefresh) and usually contains user info (name, id, etc), and also an epoch date value which represents the expiration date of the token.第二个字符串是您的“有效负载”(此处为 tokenObjAccess 或 tokenObjRefresh),通常包含用户信息(姓名、ID 等),以及表示令牌到期日期的纪元日期值。 That payload obj, like the first, is passed through JSON.stringify(), then converted to a base64URL value.该有效负载 obj 与第一个一样,通过 JSON.stringify() 传递,然后转换为 base64URL 值。 DO NOT put sensitive data in these first two objs, they are not "encrypted" at all.不要将敏感数据放在前两个对象中,它们根本没有“加密”。 Base64 encoding can be reversed by anyone with a computer and Google.任何拥有计算机和 Google 的人都可以反转 Base64 编码。

The third string is the jwt "signature."第三个字符串是 jwt “签名”。 It is created by concatenating the first two base64 strings with a period (.) in the middle, then passing them through the HS256 encryption algorithm (HMAC-SHA256).它是通过将前两个 base64 字符串与中间的句点 (.) 连接起来,然后将它们传递给 HS256 加密算法 (HMAC-SHA256) 来创建的。

Then all three strings (two base64URL strings and the HS256 encrypted string) are concatenated: base64URL(tokenObjBase) + '.'然后连接所有三个字符串(两个 base64URL 字符串和 HS256 加密字符串):base64URL(tokenObjBase) + '.' + base64URL(tokenObjPayload) + '.' + base64URL(tokenObjPayload) + '.' + signatureHS256 + 签名HS256

Hope this helps anyone having issues with jwt permanently logging their mirage users out of their Ember applications!希望这可以帮助任何遇到 jwt 问题的人将他们的 Mirage 用户从 Ember 应用程序中永久注销!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM