UNPKG

lin-mizar

Version:
272 lines (271 loc) 8.74 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.verifyRefreshToken = exports.verifyAccessToken = exports.createRefreshToken = exports.createAccessToken = exports.parseHeader = exports.getTokens = exports.jwt = exports.Token = void 0; const tslib_1 = require("tslib"); const jsonwebtoken_1 = tslib_1.__importStar(require("jsonwebtoken")); const exception_1 = require("../exception"); const lodash_1 = require("lodash"); const utils_1 = require("../utils"); const config_1 = require("../config"); /** * 令牌类,提供令牌的生成和解析功能 * * ```js * const jwt = new Token( * config.getItem("secret"), * config.getItem("accessExp"), * config.getItem("refreshExp") * ); * ``` */ class Token { /** * 构造函数 * @param secret 牌的secret值 * @param accessExp access token 过期时间 * @param refreshExp refresh token 过期时间 */ constructor(secret, accessExp, refreshExp) { /** * access token 默认的过期时间 */ this.accessExp = 60 * 60; // 1h; /** * refresh token 默认的过期时间 */ this.refreshExp = 60 * 60 * 24 * 30 * 3; // 3 months secret && (this.secret = secret); refreshExp && (this.refreshExp = refreshExp); accessExp && (this.accessExp = accessExp); } /** * 挂载到 ctx 上 */ initApp(app, secret, accessExp, refreshExp) { // 将 jwt 实例挂到 app 的 context 上 app.context.jwt = this; secret && (this.secret = secret); refreshExp && (this.refreshExp = refreshExp); accessExp && (this.accessExp = accessExp); } /** * 生成access_token * @param identity 标识位 */ createAccessToken(identity) { if (!this.secret) { throw new Error('secret can not be empty'); } let exp = Math.floor(Date.now() / 1000) + this.accessExp; return jsonwebtoken_1.default.sign({ exp, identity, scope: 'lin', type: utils_1.TokenType.ACCESS }, this.secret); } /** * 生成refresh_token * @param identity 标识位 */ createRefreshToken(identity) { if (!this.secret) { throw new Error('secret can not be empty'); } let exp = Math.floor(Date.now() / 1000) + this.refreshExp; return jsonwebtoken_1.default.sign({ exp, identity, scope: 'lin', type: utils_1.TokenType.REFRESH }, this.secret); } /** * verifyToken 验证token * 若过期,抛出ExpiredTokenException * 若失效,抛出InvalidTokenException * * @param token 令牌 */ verifyToken(token, type = utils_1.TokenType.ACCESS) { if (!this.secret) { throw new Error('secret can not be empty'); } // NotBeforeError // TokenExpiredError let decode; try { decode = jsonwebtoken_1.default.verify(token, this.secret); } catch (error) { if (error instanceof jsonwebtoken_1.TokenExpiredError) { if (type === utils_1.TokenType.ACCESS) { throw new exception_1.ExpiredTokenException({ code: 10051 }); } else if (type === utils_1.TokenType.REFRESH) { throw new exception_1.ExpiredTokenException({ code: 10052 }); } else { throw new exception_1.ExpiredTokenException(); } } else { if (type === utils_1.TokenType.ACCESS) { throw new exception_1.InvalidTokenException({ code: 10041 }); } else if (type === utils_1.TokenType.REFRESH) { throw new exception_1.InvalidTokenException({ code: 10042 }); } else { throw new exception_1.InvalidTokenException(); } } } return decode; } } exports.Token = Token; /** * jwt 的实例 */ const jwt = new Token(config_1.config.getItem('secret'), config_1.config.getItem('accessExp'), config_1.config.getItem('refreshExp')); exports.jwt = jwt; /** * 生成access token * @param payload 负载,支持 string 和 object * @param options 参数 */ function createAccessToken(payload, options) { // type: TokenType.REFRESH let exp = Math.floor(Date.now() / 1000) + jwt.accessExp; if (typeof payload === 'string') { return jsonwebtoken_1.default.sign({ indentify: payload, type: utils_1.TokenType.ACCESS, exp: jwt.accessExp }, jwt.secret, options); } else { return jsonwebtoken_1.default.sign(Object.assign(Object.assign({}, payload), { type: utils_1.TokenType.ACCESS, exp: exp }), jwt.secret, options); } } exports.createAccessToken = createAccessToken; /** * 生成refresh token * @param payload 负载,支持 string 和 object * @param options 参数 */ function createRefreshToken(payload, options) { let exp = Math.floor(Date.now() / 1000) + jwt.refreshExp; // type: TokenType.REFRESH if (typeof payload === 'string') { return jsonwebtoken_1.default.sign({ indentify: payload, type: utils_1.TokenType.REFRESH, exp: jwt.refreshExp }, jwt.secret, options); } else { return jsonwebtoken_1.default.sign(Object.assign(Object.assign({}, payload), { type: utils_1.TokenType.REFRESH, exp: exp }), jwt.secret, options); } } exports.createRefreshToken = createRefreshToken; /** * 验证 access token * @param token 令牌 * @param options 选项 */ function verifyAccessToken(token, options) { let decode; try { decode = jsonwebtoken_1.default.verify(token, jwt.secret, options); } catch (error) { if (error instanceof jsonwebtoken_1.TokenExpiredError) { throw new exception_1.ExpiredTokenException({ code: 10051 }); } else { throw new exception_1.InvalidTokenException({ code: 10041 }); } } if (!decode['type'] || decode['type'] !== utils_1.TokenType.ACCESS) { throw new exception_1.InvalidTokenException(); } return decode; } exports.verifyAccessToken = verifyAccessToken; /** * 验证 refresh token * @param token 令牌 * @param options 选项 */ function verifyRefreshToken(token, options) { let decode; try { decode = jsonwebtoken_1.default.verify(token, jwt.secret, options); } catch (error) { if (error instanceof jsonwebtoken_1.TokenExpiredError) { throw new exception_1.ExpiredTokenException({ code: 10052 }); } else { throw new exception_1.InvalidTokenException({ code: 10042 }); } } if (!decode['type'] || decode['type'] !== utils_1.TokenType.REFRESH) { throw new exception_1.InvalidTokenException(); } return decode; } exports.verifyRefreshToken = verifyRefreshToken; /** * 颁发令牌 * @param user 用户 */ function getTokens(user) { const accessToken = jwt.createAccessToken(user.id); const refreshToken = jwt.createRefreshToken(user.id); return { accessToken, refreshToken }; } exports.getTokens = getTokens; /** * 解析请求头 * @param ctx koa 的context * @param type 令牌的类型 */ function parseHeader(ctx, type = utils_1.TokenType.ACCESS) { // 此处借鉴了koa-jwt if (!ctx.header || !ctx.header.authorization) { ctx.throw(new exception_1.AuthFailed({ code: 10013 })); } const parts = ctx.header.authorization.split(' '); if (parts.length === 2) { // Bearer 字段 const scheme = parts[0]; // token 字段 const token = parts[1]; if (/^Bearer$/i.test(scheme)) { // @ts-ignore const obj = ctx.jwt.verifyToken(token, type); if (!lodash_1.get(obj, 'type') || lodash_1.get(obj, 'type') !== type) { ctx.throw(new exception_1.AuthFailed({ code: 10250 })); } if (!lodash_1.get(obj, 'scope') || lodash_1.get(obj, 'scope') !== 'lin') { ctx.throw(new exception_1.AuthFailed({ code: 10251 })); } return obj; } } else { ctx.throw(new exception_1.AuthFailed()); } } exports.parseHeader = parseHeader;