UNPKG

titbit-toolkit

Version:

titbit框架的工具集,包括跨域、静态资源处理,权限过滤,请求计时,cookie,session,jwt等大量中间件

218 lines (177 loc) 4.04 kB
'use strict' /** * * jwt格式: * * base64UrlEncoded(header).base64UrlEncoded(payload).signature * * header: { * "alg": "hs256", * "typ": "JWT" * } * * payload是任何object类型的数据 * * payload 示例 { * "id": "1234", * "name": "okk" * } * * signature是hamacsha256(base64UrlEncoded(header).base64UrlEncoded(payload), key) * */ const randstring = require('./__randstring.js') const crypto = require('node:crypto') class JWT { constructor(options = {}) { this.expires = 3600000 this.autoTimeout = true this.header = '' this.algMap = { HS256: 'sha256', HS384: 'sha384', HS512: 'sha512', SM3: 'sm3', SHA256: 'sha256', SHA512: 'sha512', SHA384: 'sha384' } this.algKeys = Object.keys(this.algMap) Object.defineProperty(this, '__key__', { value: randstring(16), enumerable: false, configurable: false, writable: true }) Object.defineProperty(this, '__alg__', { value: 'SM3', enumerable: false, configurable: false, writable: true }) Object.defineProperties(this, { key: { get: () => { return this.__key__ }, set: (key) => { this.__key__ = key this.makeHeader() } }, alg: { get: () => { return this.__alg__ }, set: (a) => { a = a.toUpperCase() if (this.algKeys.indexOf(a) >= 0) { this.__alg__ = a this.makeHeader() } else { console.error(`无法支持的算法:${a}`) } } } }) for (let k in options) { switch(k) { case 'expires': case 'autoTimeout': case 'alg': case 'key': this[k] = options[k]; break; } } this.makeHeader() } makeHeader() { let hdata = `{"alg":"${this.__alg__}","typ":"JWT"}` this.header = Buffer.from(hdata).toString('base64url') } make(data) { if (typeof data === 'object') { if (this.autoTimeout) { data.__timeout__ = Date.now() + this.expires } data = JSON.stringify(data) } let org_str = `${this.header}.${Buffer.from(data).toString('base64url')}` return `${org_str}.${this.sign(org_str, this.algMap[this.__alg__])}` } sign(org_str, a = 'sm3') { let h = crypto.createHmac(a, this.__key__) h.update(org_str) return h.digest('base64url') } verify(token) { let arr = token.split('.') if (arr.length !== 3) { return { ok: false, errcode: 'ILLEGAL' } } let alg = this.__alg__ try { let header = JSON.parse(Buffer.from(arr[0], 'base64url').toString('utf8')) alg = header.alg } catch (err) { return { ok: false, errcode: 'ERR_HEADER' } } let hs = this.algMap[alg] if (!hs) { return { ok: false, errcode: 'UNKNOW_ALG' } } let hstr = this.sign(`${arr[0]}.${arr[1]}`, hs) if (hstr !== arr[2]) { return { ok: false, errcode: 'FAILED' } } let data = Buffer.from(arr[1], 'base64url').toString('utf8') try { data = JSON.parse(data) } catch (err) { return { ok: false, errcode: 'ERR_DATA' } } if (data.__timeout__ !== undefined && (Date.now() > data.__timeout__)) { return { ok: false, errcode: 'TIMEOUT' } } return { ok: true, data: data } } mid() { let self = this return async (ctx, next) => { let token = ctx.headers.authorization || ctx.query.token if (!token) { return ctx.status(401).send('unauthorized') } let r = self.verify(token) if (!r.ok) { return ctx.status(401).send(r.errcode) } ctx.box.user = r.data ctx.user = r.data await next() } } } module.exports = JWT