lin-mizar
Version:
The core library of Lin CMS
272 lines (271 loc) • 8.74 kB
JavaScript
;
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;