简介:
JWT 即:JSON Web Token,定义了一种紧凑的、自包含的方式,用于在网络应用环境间以 JSON 对象安全地传输信息。
JWT 是一个开放的行业标准 RFC 7519。JWT 传输的信息可以被验证和信任,因为它经过了数字签名。
JWT 一般被用来在身份提供者和服务提供者间传递被认证用户的身份信息,以便于从资源服务器获取资源,也可以增加一些额外的业务逻辑所需的声明信息。
JWT 常用于代替 Session,用于识别用户身份。传统上使用 Session 机制区别用户身份有两个缺点:一是占用服务器的存储资源,二是在集群部署下设计会非常复杂。JWT 完全可以解决 Session 方案存在的问题。
一,安装
$ npm install jsonwebtoken
二,用法
jwt.sign(payload, secretOrPrivateKey, [options, callback])
(异步)如果提供了回调,则使用err或JWT调用回调。
(同步)以字符串形式返回JsonWebToken
有效载荷可以是表示有效JSON的对象文字、缓冲区或字符串。
请注意,只有当有效载荷是对象文字时,才会设置exp或任何其他声明。未检查缓冲区或字符串有效负载的JSON有效性。
如果有效负载不是缓冲区或字符串,则会使用JSON.stringify将其强制转换为字符串。
secretOrPrivateKey是一个字符串(utf-8编码)、缓冲区、对象或KeyObject,包含HMAC算法的秘密或RSA和ECDSA的PEM编码私钥。如果是带有密码短语的私钥,则可以使用对象{key,passphrase}(基于加密文档),在这种情况下,请确保您通过了算法选项。使用RSA算法进行签名时,最小模数长度为2048,除非allowInsecureKeySizes选项设置为true。低于此大小的私钥将被拒绝,并出现错误。
选项:
算法(默认值:HS256)
expiresIn:以秒或描述时间跨度vercel/ms的字符串表示。
例如:60,“2天”,“10小时”,“7天”。数值被解释为秒计数。如果使用字符串,请确保提供时间单位(天、小时等),否则默认使用毫秒单位(“120”等于“120ms”)。
notBefore:以秒或描述时间跨度vercel/ms的字符串表示。
例如:60,“2天”,“10小时”,“7天”。数值被解释为秒计数。如果使用字符串,请确保提供时间单位(天、小时等),否则默认使用毫秒单位(“120”等于“120ms”)。
audience; issuer;jwtid; subject;noTimestamp;header;keyid
mutatePayload:如果为true,sign函数将直接修改payload对象。如果在对有效负载应用声明之后但在将其编码为令牌之前需要对其进行原始引用,则这一点非常有用。
allowInsecureKeySizes:如果为true,则允许模数低于2048的私钥用于RSA
allowInvalidAsymmetricKeyTypes:如果为true,则允许与指定算法不匹配的非对称密钥。此选项仅用于向后兼容,应避免使用。
expiresIn、notBefore、audience、subject、issuer没有默认值。这些声明也可以分别用exp、nbf、aud、sub和iss直接提供在有效载荷中,但不能同时包含在这两个位置。
请记住,exp、nbf和iat是NumericDate,请参阅相关的Token Expiration(exp声明)
标头可以通过options.header对象进行自定义。
默认情况下,生成的jwt将包括iat(在发布)声明,除非未指定时间戳。如果在有效载荷中插入iat,则将使用它而不是实际的时间戳来计算其他事情,如在options.exiresIn中给定时间跨度的exp。
默认同步签名(HMAC SHA256)
var jwt = require('jsonwebtoken');
var token = jwt.sign({ foo: 'bar' }, 'shhhhh');
异步签名
jwt.sign({ foo: 'bar' }, privateKey, { algorithm: 'RS256' }, function(err, token) {
console.log(token);
});
回溯30秒
var older_token=jwt.sign({foo:'bar',iat:Math.floor(Date.now()/1000)-30},'shhh')
代币到期(exp索赔)
JWT的标准定义了过期的exp索赔。过期时间表示为NumericDate:
JSON数值,表示从1970-01-01T00:00:00Z UTC到指定的UTC日期/时间的秒数,忽略闰秒。这相当于IEEE Std 1003.1,2013版[POSIX.1]定义的“自大纪元以来的秒数”,其中每天精确地计算86400秒,而不是可以表示非整数值。有关日期/时间,特别是UTC的详细信息,请参阅RFC 3339[RFC3339]。
这意味着exp字段应该包含自epoch以来的秒数。
正在签署过期1小时的令牌:
jwt.sign({
exp: Math.floor(Date.now() / 1000) + (60 * 60),
data: 'foobar'
}, 'secret');
使用此库生成类似令牌的另一种方法是:
jwt.sign({
data: 'foobar'
}, 'secret', { expiresIn: 60 * 60 });
//甚至更好:
jwt.sign({
data: 'foobar'
}, 'secret', { expiresIn: '1h' });
jwt.verify(token, secretOrPublicKey, [options, callback])
(异步)如果提供了回调,函数将异步执行。如果签名有效并且可选的过期、访问群体或颁发者有效,则使用解码的有效负载调用回调。如果没有,它将被调用并返回错误。
(同步)如果没有提供回调,函数将同步执行。如果签名有效并且可选的过期、访问群体或颁发者有效,则返回解码的有效负载。否则,它将抛出错误。
警告:当令牌来自不受信任的来源(例如用户输入或外部请求)时,应将返回的解码有效载荷视为任何其他用户输入;请确保进行消毒,并且只使用预期的属性
token是JsonWebToken字符串
secretOrPublicKey是一个字符串(utf-8编码)、缓冲区或KeyObject,包含HMAC算法的秘密,或RSA和ECDSA的PEM编码公钥。如果jwt.verify是异步调用的,secretOrPublicKey可以是一个应该获取密钥或公钥的函数。请参阅下面的详细示例
如本评论所述,还有其他库需要base64编码的机密(使用base64编码随机字节),如果是这种情况,您可以传递Buffer.from(secret,'base64'),通过这样做,机密将使用base64解码,令牌验证将使用原始随机字节。
选项
algorithms:包含允许的算法名称的字符串列表。例如,[“HS256”,“HS384”]。
如果未指定,将根据提供的密钥类型使用默认值
secret-['HS256','HS384','HS512']
rsa-['RS256','RS384','RS512']
ec-【'ES256’、'ES384’、'ES 512’】
默认值-['RS256'、'RS384'、'RSM12']
audience:如果您想检查audience(aud),请在此处提供一个值。可以对照字符串、正则表达式或字符串和/或正则表达式的列表来检查受众。
例如:“urn:foo”,/un:f[o]{2}/,[/urn:f[o]{2}-,“urn:bar”]
complete:返回一个具有解码的{payload,header,signature}的对象,而不是仅返回有效载荷的常规内容。
isser(可选):iss字段的有效值的字符串或字符串数组。
jwtid(可选):如果要检查JWT ID(jti),请在此处提供一个字符串值。
ignoreExpiration:如果为true,则不验证令牌的过期时间。
ignoreNotBefore。。。
subject:如果要检查subject,请在此处提供值
clockTolerance:检查nbf和exp声明时允许的秒数,用于处理不同服务器之间的小时钟差异
maxAge:允许令牌仍然有效的最大年龄。它以秒或描述时间跨度vercel/ms的字符串表示。
例如:1000,“2天”,“10小时”,“7天”。数值被解释为秒计数。如果使用字符串,请确保提供时间单位(天、小时等),否则默认使用毫秒单位(“120”等于“120ms”)。
clockTimestamp:应用作所有必要比较的当前时间的时间(以秒为单位)。
nonce:如果要检查nonce声明,请在此处提供一个字符串值。它用于ID令牌的Open ID。(Open ID实施说明)
allowInvalidAsymmetricKeyTypes:如果为true,则允许与指定算法不匹配的非对称密钥。此选项仅用于向后兼容,应避免使用。
// verify a token symmetric - synchronous
var decoded = jwt.verify(token, 'shhhhh');
console.log(decoded.foo) // bar
// verify a token symmetric
jwt.verify(token, 'shhhhh', function(err, decoded) {
console.log(decoded.foo) // bar
});
// invalid token - synchronous
try {
var decoded = jwt.verify(token, 'wrong-secret');
} catch(err) {
// err
}
// invalid token
jwt.verify(token, 'wrong-secret', function(err, decoded) {
// err
// decoded undefined
});
// verify a token asymmetric
var cert = fs.readFileSync('public.pem'); // get public key
jwt.verify(token, cert, function(err, decoded) {
console.log(decoded.foo) // bar
});
// verify audience
var cert = fs.readFileSync('public.pem'); // get public key
jwt.verify(token, cert, { audience: 'urn:foo' }, function(err, decoded) {
// if audience mismatch, err == invalid audience
});
// verify issuer
var cert = fs.readFileSync('public.pem'); // get public key
jwt.verify(token, cert, { audience: 'urn:foo', issuer: 'urn:issuer' }, function(err, decoded) {
// if issuer mismatch, err == invalid issuer
});
// verify jwt id
var cert = fs.readFileSync('public.pem'); // get public key
jwt.verify(token, cert, { audience: 'urn:foo', issuer: 'urn:issuer', jwtid: 'jwtid' }, function(err, decoded) {
// if jwt id mismatch, err == invalid jwt id
});
// verify subject
var cert = fs.readFileSync('public.pem'); // get public key
jwt.verify(token, cert, { audience: 'urn:foo', issuer: 'urn:issuer', jwtid: 'jwtid', subject: 'subject' }, function(err, decoded) {
// if subject mismatch, err == invalid subject
});
// alg mismatch
var cert = fs.readFileSync('public.pem'); // get public key
jwt.verify(token, cert, { algorithms: ['RS256'] }, function (err, payload) {
// if token alg != RS256, err == invalid signature
});
// Verify using getKey callback
// Example uses https://github.com/auth0/node-jwks-rsa as a way to fetch the keys.
var jwksClient = require('jwks-rsa');
var client = jwksClient({
jwksUri: 'https://sandrino.auth0.com/.well-known/jwks.json'
});
function getKey(header, callback){
client.getSigningKey(header.kid, function(err, key) {
var signingKey = key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
});
}
jwt.verify(token, getKey, options, function(err, decoded) {
console.log(decoded.foo) // bar
});