jwt(json web token)入门,node实现简单jwt
// 全部代码
const mycrypto = require("node:crypto");
const sign = (data, secret) => {
const header = {"alg": "sha256", "type": "jwt"};
const content = [
base64Encode(header),
base64Encode(data)
];
const signature = mycrypto.createHmac('sha256', secret)
.update(content.join("."), secret).digest('base64');
return fromBase64([...content, signature].join("."));
};
function toBase64(str){
const newStr = str.replace("/_/g","/").replace("/-/g","+");
const len = newStr.length;
const padLen = len%4 ? (4 - (len%4)) : 0;
return newStr + new Array(padLen).fill("=").join("");
}
function fromBase64(data){
return data.replace("/\+/g","-")
.replace("/\//g","_").replace("/=/g","");
}
function base64Encode(data){
return Buffer.from(JSON.stringify(data))
.toString("base64");
}
function verify(token, secret) {
const [header, payload, signature] = token.split(".");
const newSignature = mycrypto.createHmac("sha256", secret).update(
[toBase64(header), toBase64(payload)].join('.')
).digest('base64');
console.log(signature, newSignature);
if(signature === fromBase64(newSignature)){
return true;
}
return false;
}
const token = sign("acsa","xx")
console.log(verify(token, "xx"));
jwt是用来校验用户状态的工具
jwt解决的问题:
jwt放在url中或者header中不会有跨域问题,而且服务端不用保存会话数据,每次校验即可,而cookie在多个域名下会有跨域问题,sessionId放到cookie中时,服务端要存储sessionId,服务端分布式部署时要解决共享session问题
jwt的原理
登录时,服务端校验用户名和密码后,根据算法将用户标识生成token,返回前端,前端在后面的请求中携带token,后端进行鉴权
jwt的数据结构
- header {"alg":"SHA256", "type": "jwt"} alg(algorithm)是指使用的算法,
- payload 官方定义了7个字段,此外还可以自定义字段
- iss(issue):签发人
- exp:(expiration time):过期时间
- sub(subject): 主题
- aud(audience): 受众
- nbf(not before): 生效时间
- iat:(issue at): 签发时间
- jti:(jwt id): 编号
- 自定义 例如{"userId":123}
- signature 对前两部分进行签名
HMACSHA256(base64Encode(header)+"."+base64Encode(payload),secret);
- secret是字符串加密的key
- 生成的token是有可能放到url中的,而base64有3个字符+/=在url里面有特殊的含义,所以在转成base64时要把这三个字符替换一下,
- "+" -> "-"
- "=" -> ""
- "/" -> "_"
function base64Encode(data){
return Buffer.from(JSON.stringify(data))
.toString("base64");
}
function fromBase64(data){
return data.replace("/\+/g","-")
.replace("/\//g","_").replace("/=/g","");
}
function toBase64(str){
const newStr = str.replace("/_/g","/").replace("/-/g","+");
const len = newStr.length;
const padLen = len%4 ? (4 - (len%4)) : 0;
return newStr + new Array(padLen).fill("=").join("");
}
实现原理
使用nodejs内置的cryto模块
const crypto = require("crypto");
const sign = (data, secret) => {
const header = {"alg": "sha256", "type": "jwt"};
const content = [
base64Encode(header),
base64Encode(data)
];
const signature = crypto.createHmac('sha256', secret)
.update(content.join("."), secret).digest('base64');
return fromBase64([...content, signature].join("."));
};
const verify = (token,secret) => (token, secret) {
const [header, payload, signature] = token.split(".");
const newSignature = mycrypto.createHmac("sha256", secret).update(
[toBase64(header), toBase64(payload)].join('.')
).digest('base64');
console.log(signature, newSignature);
if(signature === fromBase64(newSignature)){
return true;
}
return false;
}
jwt缺点
服务端不保存会话状态,所以jwt会在有效期内会一直有效,一旦泄露,任何人都可以获得jwt认证的权限,为了减少盗用jwt设置的有效时间不宜设置过长,重要操作一定要进行身份验证,建议使用https协议传输jwt