crypto-js vs jose vs jsonwebtoken vs jws vs node-jose
前端加密与 JWT 库选型指南
crypto-jsjosejsonwebtokenjwsnode-jose类似的npm包:

前端加密与 JWT 库选型指南

crypto-js、jose、jsonwebtoken、jws 和 node-jose 都是 JavaScript 生态中用于处理加密、哈希和 JSON Web Token(JWT)的流行库,但它们的设计目标、适用场景和维护状态存在显著差异。crypto-js 是一个通用的前端加密工具箱,支持多种加密算法,但不处理 JWT。其余四个库专注于 JWT 相关功能,其中 jose 是现代、标准兼容且跨平台的首选,jsonwebtoken 主要面向 Node.js 且不支持加密令牌(JWE),jws 是底层签名库通常不直接用于应用开发,而 node-jose 已被官方弃用,不应在新项目中使用。

npm下载趋势

3 年

GitHub Stars 排名

统计详情

npm包名称
下载量
Stars
大小
Issues
发布时间
License
crypto-js016,400487 kB2793 年前MIT
jose07,830211 kB01 个月前MIT
jsonwebtoken018,18043.4 kB21210 个月前MIT
jws072118.8 kB3310 个月前MIT
node-jose0720353 kB744 年前Apache-2.0

前端加密与 JWT 库深度对比:crypto-js、jose、jsonwebtoken、jws 与 node-jose

在现代 Web 应用中,处理加密、哈希、数字签名和 JSON Web Token(JWT)是常见需求。然而,前端开发者面对多个 npm 包时,常因功能重叠、API 差异或维护状态而难以抉择。本文将深入比较 crypto-js、jose、jsonwebtoken、jws 和 node-jose,从实际工程角度出发,帮助你做出合理的技术选型。

⚠️ 重要前提:部分库已弃用

首先必须明确:node-jose 已被官方标记为弃用。其 GitHub 仓库和 npm 页面均指出,该库不再维护,推荐迁移到 jose。因此,新项目绝不应使用 node-jose。

同样,jws 是一个底层签名库,通常不直接用于应用层开发,而是作为 jsonwebtoken 的依赖存在。除非你正在实现自定义 JWT 签名逻辑,否则一般无需直接引入它。

🔐 核心用途区分:通用加密 vs JWT 专用

这些库可分为两类:

  • 通用加密/哈希工具:crypto-js
  • JWT 相关库:jose、jsonwebtoken、jws、node-jose(已弃用)

crypto-js:前端友好的加密工具箱

crypto-js 是一个纯 JavaScript 实现的加密库,支持 AES、DES、SHA、HMAC、PBKDF2 等多种算法。它专为浏览器环境设计,无需 Node.js 内置模块,适合在前端进行本地加密、哈希或密钥派生。

// crypto-js:AES 加密
import CryptoJS from 'crypto-js';

const message = 'Hello, world!';
const key = 'secret-key-123';
const encrypted = CryptoJS.AES.encrypt(message, key).toString();
const decrypted = CryptoJS.AES.decrypt(encrypted, key).toString(CryptoJS.enc.Utf8);

注意:crypto-js 不支持 JWT。它无法解析、验证或签发 JWT,仅提供底层加密原语。

JWT 专用库:谁更适合前端?

JWT 库的核心能力包括:

  • 签发(sign)
  • 验证(verify)
  • 解码(decode)
  • 支持 JWS(签名)、JWE(加密)等标准

我们逐一分析。

🧪 JWT 签发与验证:API 与兼容性对比

jsonwebtoken:Node.js 优先,前端需谨慎

jsonwebtoken 是最流行的 JWT 库之一,但主要面向 Node.js 环境。它依赖 Node.js 的 crypto 模块,在浏览器中需通过 polyfill(如 @peculiar/webcrypto + bundler 配置)才能运行,且不支持 JWE(加密令牌)。

// jsonwebtoken:签发 JWT(Node.js 示例)
import jwt from 'jsonwebtoken';

const token = jwt.sign({ id: 123 }, 'secret', { expiresIn: '1h' });
const decoded = jwt.verify(token, 'secret');

在前端使用时,若未正确配置 polyfill,会报错 Cannot find module 'crypto'。此外,它仅支持对称密钥(HMAC)和非对称密钥(RSA/ECDSA)的 JWS,不支持 JWE。

jose:现代、标准兼容、全平台支持

jose 是当前最推荐的 JWT 库。它完全遵循 IETF 的 JOSE(JSON Object Signing and Encryption)标准,支持 JWS 和 JWE,并且原生支持浏览器和 Node.js,无需额外 polyfill。其 API 设计清晰,支持 Web Crypto API。

// jose:签发 JWS(浏览器/Node.js 通用)
import { SignJWT } from 'jose';

const secret = new TextEncoder().encode('secret');
const token = await new SignJWT({ id: 123 })
  .setProtectedHeader({ alg: 'HS256' })
  .setIssuedAt()
  .setExpirationTime('1h')
  .sign(secret);

// 验证
import { jwtVerify } from 'jose';
const { payload } = await jwtVerify(token, secret);

jose 还支持公钥/私钥对(如 RS256、ES256)和加密令牌(JWE),适合需要高安全性的场景。

jws:底层签名,不推荐直接使用

jws 仅提供 JWS(JSON Web Signature)的底层实现,用于创建和验证签名,但不处理 JWT 的声明(claims)或过期逻辑。通常作为 jsonwebtoken 的内部依赖存在。

// jws:手动构建 JWS
import jws from 'jws';

const header = { alg: 'HS256' };
const payload = { id: 123, iat: Math.floor(Date.now() / 1000) };
const signingInput = [header, payload].map(JSON.stringify).join('.');
const signature = /* 手动 HMAC-SHA256 */;
const token = `${signingInput}.${signature}`;

// 验证需手动解析并比对签名

这种方式繁琐且易出错,不建议在应用层直接使用 jws。

node-jose:已弃用,切勿使用

如前所述,node-jose 官方已弃用。其 API 虽然功能完整(支持 JWS/JWE),但存在安全漏洞风险且无后续更新。所有新项目应选择 jose 作为替代。

🌐 浏览器兼容性与打包考量

  • crypto-js:纯 JS,无依赖,打包后体积可控,适合前端。
  • jose:使用现代 ES 模块,支持 tree-shaking,浏览器原生可用(依赖 Web Crypto API,现代浏览器均支持)。
  • jsonwebtoken:依赖 Node.js crypto,在前端需额外配置,增加构建复杂度。
  • jws / node-jose:前者为底层库,后者已弃用,均不推荐用于新项目。

🔑 安全实践建议

  • 永远不要在前端存储 JWT 密钥:无论是对称密钥还是私钥,都不应暴露在客户端代码中。前端只能用于验证由服务端签发的令牌(使用公钥)或解码无敏感信息的 payload。
  • 优先使用 jose 进行验证:若需在前端验证 JWT(例如检查过期时间),jose 提供了安全、标准的 API。
// 前端安全验证示例(使用公钥)
import { jwtVerify } from 'jose';

const publicKey = await crypto.subtle.importKey(
  'jwk',
  { kty: 'RSA', ... }, // 来自服务端的 JWK
  { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' },
  false,
  ['verify']
);

const { payload } = await jwtVerify(token, publicKey);

📊 总结:如何选择?

场景推荐库理由
前端本地加密/哈希(如密码哈希、AES 加密)crypto-js纯 JS,无依赖,浏览器友好
签发或验证 JWT(全平台)jose标准兼容,支持 JWS/JWE,原生浏览器支持
Node.js 后端签发 JWT(简单场景)jsonwebtoken社区广泛,API 简洁(但注意不支持 JWE)
需要 JWE(加密令牌)jose唯一活跃且标准兼容的选择
新项目避免 node-jose 和 jws前者已弃用,后者过于底层

💡 最终建议

  • 前端项目:若需处理 JWT,首选 jose;若只需加密/哈希,用 crypto-js。
  • 全栈项目:前后端统一使用 jose 可减少上下文切换,提升一致性。
  • 遗留系统迁移:将 node-jose 或 jsonwebtoken(需 JWE 时)逐步替换为 jose。

记住:安全始于正确的工具选择。在加密和身份验证领域,使用活跃维护、标准兼容的库是保障应用安全的第一步。

如何选择: crypto-js vs jose vs jsonwebtoken vs jws vs node-jose

  • crypto-js:

    选择 crypto-js 如果你需要在前端执行通用的加密、解密、哈希或密钥派生操作(如 AES、SHA、HMAC),且不需要处理 JWT。它是一个纯 JavaScript 库,无需 Node.js 环境,适合浏览器场景,但要注意它不提供任何 JWT 功能。

  • jose:

    选择 jose 如果你需要在浏览器或 Node.js 中处理 JWT(包括签发、验证、解码),尤其是需要支持 JWS(签名)和 JWE(加密)标准的现代应用。它是目前最活跃、最标准兼容的库,原生支持 Web Crypto API,适合新项目和需要高安全性的场景。

  • jsonwebtoken:

    选择 jsonwebtoken 仅当你在 Node.js 后端开发中需要快速实现简单的 JWT 签发和验证,且不需要 JWE(加密令牌)功能。它在前端使用需额外配置 polyfill,不适合浏览器环境,且缺乏对现代 JOSE 标准的完整支持。

  • jws:

    选择 jws 仅当你需要直接操作 JWS(JSON Web Signature)的底层签名逻辑,例如构建自定义 JWT 实现或调试签名过程。它不处理 JWT 声明(如过期时间)或验证逻辑,通常作为其他 JWT 库的依赖存在,不推荐在应用层直接使用。

  • node-jose:

    不要选择 node-jose。该库已被官方标记为弃用,不再维护,存在潜在安全风险。所有新项目应使用 jose 作为替代,现有项目应制定迁移计划。

crypto-js的README

crypto-js

JavaScript library of crypto standards.

Discontinued

Active development of CryptoJS has been discontinued. This library is no longer maintained.

Nowadays, NodeJS and modern browsers have a native Crypto module. The latest version of CryptoJS already uses the native Crypto module for random number generation, since Math.random() is not crypto-safe. Further development of CryptoJS would result in it only being a wrapper of native Crypto. Therefore, development and maintenance has been discontinued, it is time to go for the native crypto module.

Node.js (Install)

Requirements:

  • Node.js
  • npm (Node.js package manager)
npm install crypto-js

Usage

ES6 import for typical API call signing use case:

import sha256 from 'crypto-js/sha256';
import hmacSHA512 from 'crypto-js/hmac-sha512';
import Base64 from 'crypto-js/enc-base64';

const message, nonce, path, privateKey; // ...
const hashDigest = sha256(nonce + message);
const hmacDigest = Base64.stringify(hmacSHA512(path + hashDigest, privateKey));

Modular include:

var AES = require("crypto-js/aes");
var SHA256 = require("crypto-js/sha256");
...
console.log(SHA256("Message"));

Including all libraries, for access to extra methods:

var CryptoJS = require("crypto-js");
console.log(CryptoJS.HmacSHA1("Message", "Key"));

Client (browser)

Requirements:

  • Node.js
  • Bower (package manager for frontend)
bower install crypto-js

Usage

Modular include:

require.config({
    packages: [
        {
            name: 'crypto-js',
            location: 'path-to/bower_components/crypto-js',
            main: 'index'
        }
    ]
});

require(["crypto-js/aes", "crypto-js/sha256"], function (AES, SHA256) {
    console.log(SHA256("Message"));
});

Including all libraries, for access to extra methods:

// Above-mentioned will work or use this simple form
require.config({
    paths: {
        'crypto-js': 'path-to/bower_components/crypto-js/crypto-js'
    }
});

require(["crypto-js"], function (CryptoJS) {
    console.log(CryptoJS.HmacSHA1("Message", "Key"));
});

Usage without RequireJS

<script type="text/javascript" src="path-to/bower_components/crypto-js/crypto-js.js"></script>
<script type="text/javascript">
    var encrypted = CryptoJS.AES(...);
    var encrypted = CryptoJS.SHA256(...);
</script>

API

See: https://cryptojs.gitbook.io/docs/

AES Encryption

Plain text encryption

var CryptoJS = require("crypto-js");

// Encrypt
var ciphertext = CryptoJS.AES.encrypt('my message', 'secret key 123').toString();

// Decrypt
var bytes  = CryptoJS.AES.decrypt(ciphertext, 'secret key 123');
var originalText = bytes.toString(CryptoJS.enc.Utf8);

console.log(originalText); // 'my message'

Object encryption

var CryptoJS = require("crypto-js");

var data = [{id: 1}, {id: 2}]

// Encrypt
var ciphertext = CryptoJS.AES.encrypt(JSON.stringify(data), 'secret key 123').toString();

// Decrypt
var bytes  = CryptoJS.AES.decrypt(ciphertext, 'secret key 123');
var decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));

console.log(decryptedData); // [{id: 1}, {id: 2}]

List of modules

  • crypto-js/core
  • crypto-js/x64-core
  • crypto-js/lib-typedarrays

  • crypto-js/md5
  • crypto-js/sha1
  • crypto-js/sha256
  • crypto-js/sha224
  • crypto-js/sha512
  • crypto-js/sha384
  • crypto-js/sha3
  • crypto-js/ripemd160

  • crypto-js/hmac-md5
  • crypto-js/hmac-sha1
  • crypto-js/hmac-sha256
  • crypto-js/hmac-sha224
  • crypto-js/hmac-sha512
  • crypto-js/hmac-sha384
  • crypto-js/hmac-sha3
  • crypto-js/hmac-ripemd160

  • crypto-js/pbkdf2

  • crypto-js/aes
  • crypto-js/tripledes
  • crypto-js/rc4
  • crypto-js/rabbit
  • crypto-js/rabbit-legacy
  • crypto-js/evpkdf

  • crypto-js/format-openssl
  • crypto-js/format-hex

  • crypto-js/enc-latin1
  • crypto-js/enc-utf8
  • crypto-js/enc-hex
  • crypto-js/enc-utf16
  • crypto-js/enc-base64

  • crypto-js/mode-cfb
  • crypto-js/mode-ctr
  • crypto-js/mode-ctr-gladman
  • crypto-js/mode-ofb
  • crypto-js/mode-ecb

  • crypto-js/pad-pkcs7
  • crypto-js/pad-ansix923
  • crypto-js/pad-iso10126
  • crypto-js/pad-iso97971
  • crypto-js/pad-zeropadding
  • crypto-js/pad-nopadding

Release notes

4.2.0

Change default hash algorithm and iteration's for PBKDF2 to prevent weak security by using the default configuration.

Custom KDF Hasher

Blowfish support

4.1.1

Fix module order in bundled release.

Include the browser field in the released package.json.

4.1.0

Added url safe variant of base64 encoding. 357

Avoid webpack to add crypto-browser package. 364

4.0.0

This is an update including breaking changes for some environments.

In this version Math.random() has been replaced by the random methods of the native crypto module.

For this reason CryptoJS might not run in some JavaScript environments without native crypto module. Such as IE 10 or before or React Native.

3.3.0

Rollback, 3.3.0 is the same as 3.1.9-1.

The move of using native secure crypto module will be shifted to a new 4.x.x version. As it is a breaking change the impact is too big for a minor release.

3.2.1

The usage of the native crypto module has been fixed. The import and access of the native crypto module has been improved.

3.2.0

In this version Math.random() has been replaced by the random methods of the native crypto module.

For this reason CryptoJS might does not run in some JavaScript environments without native crypto module. Such as IE 10 or before.

If it's absolute required to run CryptoJS in such an environment, stay with 3.1.x version. Encrypting and decrypting stays compatible. But keep in mind 3.1.x versions still use Math.random() which is cryptographically not secure, as it's not random enough.

This version came along with CRITICAL BUG.

DO NOT USE THIS VERSION! Please, go for a newer version!

3.1.x

The 3.1.x are based on the original CryptoJS, wrapped in CommonJS modules.