highlight: agate
theme: fancy
安裝
npm i egg-bag-framework
內置多種模塊,中間件以及工具
中間件
校驗sing簽名是否合法,防止隨意發起請求
'use strict';
module.exports = (option, app) => {
return async function sing(ctx, next) {
const sing = ctx.request.header.sing;
const { domain, expireTime, cache } = ctx.app.config.website;
const default_cache = 'redis';
if (sing) {
let getSing = null;
if (cache === default_cache) {
getSing = await app.redis.get(sing);
} else {
getSing = await app.lru.get(sing);
}
if (getSing) {
ctx.body = ctx.resultData({ msg: 'sing簽名已過期' }); // 在存在説明既過期
} else {
try {
const decSing = ctx.helper.aesDecrypt(sing);
const singData = JSON.parse(decSing);
if (singData.domain === domain) {
if (cache === default_cache) {
await app.redis.set(sing, 1);
} else {
await app.lru.set(sing, 1);
}
await app.redis.set(sing, 1);
await app.redis.expire(sing, expireTime);
await next();
} else {
ctx.body = ctx.resultData({ msg: 'sing簽名不合法,缺少字符串' });
}
} catch (e) {
ctx.body = ctx.resultData({ msg: 'sing簽名不合法' });
}
}
} else {
ctx.body = ctx.resultData({ msg: '缺少sing簽名' });
}
};
};
限流中間件
防止接口被惡意盜刷,如果被攻擊了,請求使用nginx或者服務器配置白黑名單
'use strict';
const { RateLimiterMemory } = require('rate-limiter-flexible'); // 限流中間件
module.exports = () => {
// 創建一個基於內存的令牌桶速率限制器,每秒限制 12 次請求
const opts = {
points: 12,
duration: 1,
};
const rateLimiter = new RateLimiterMemory(opts);
return async function limiter(ctx, next) {
rateLimiter.consume(ctx.request.ip)
.then(rateLimiterRes => {
next();
})
.catch(rateLimiterRes => {
ctx.body = ctx.resultData({ msg: '觸發限流了', code: 2001 });
});
};
};
驗證jwt
'use strict';
module.exports = () => {
return async function authority(ctx, next) {
const authorization = ctx.request.header.authorization;
if (authorization) {
try {
ctx.helper.verifyToken(authorization); // 驗證jwt
await next();
} catch (err) {
ctx.body = ctx.resultData({ msg: 'access_token過期', code: 1003 });
}
} else {
ctx.body = ctx.resultData({ msg: '缺少access_token', code: 1003 });
}
};
};
內置模塊
jwt
(JWT)是一個開放標準(RFC 7519),它定義了一種緊湊的、自包含的方式,用於作為JSON對象在各方之間安全地傳輸信息。該信息可以被驗證和信任,因為它是數字簽名的
// plugin.js
exports.jwt = {
enable: true,
package: 'egg-jwt',
};
// config.default.js
config.jwt = {
secret: 'ABCD20231017QWERYSUNXSJL', // 可以自定義
sign: {
expiresIn: 8 * 60 * 60, // 過期時間8小時
},
};
Validate
參數校驗模塊
// plugin.js
exports.validate = {
enable: true,
package: 'egg-validate',
};
// config.default.js
config.validate = {
convert: true,
translate() {
const args = Array.prototype.slice.call(arguments);
return I18n.__.apply(I18n, args);
},
};
redis
Redis是一個開源(BSD許可),內存存儲的數據結構服務器,可用作數據庫,高速緩存和消息隊列代理
// plugin.js
exports.redis = {
enable: true,
package: 'egg-redis',
};
// config.default.js
config.redis = {
client: {
port: 6379,
host: '127.0.0.1',
password: 'auth',
db: 0,
},
}
lru
本地緩存
exports.lru = {
enable: true,
package: 'egg-lru', // 本地緩存
};
config.lru = {
client: {
max: 3000, // 所有lru緩存配置可用
maxAge: 1000 * 60 * 30, // 60 min cache
},
app: true, // 加載到app中,默認是打開的
agent: false, // 加載到代理中,默認為關閉
};
上傳模式
// config.default.js
config.multipart = {
mode: 'file',
fileSize: '50mb', // 接收文件大小
whitelist: [ // 允許接收的文件類型
'.png',
'.jpg',
'.webp',
'.gif',
'.zip',
'.doc',
'.docx',
'.txt',
'.xlsx',
'.pdf',
'.mp4',
'.webm',
'.mov',
'.flv',
'.avi',
'.f4v',
'.mov',
'.m4v',
'.rmvb',
'.rm',
'.mpg',
'.mpeg',
],
};
Sequelize
Sequelize 是一個基於 promise 的 Node.js ORM, 目前支持 Postgres, MySQL, MariaDB, SQLite 以及 Microsoft SQL Server. 它具有強大的事務支持, 關聯關係, 預讀和延遲加載,讀取複製等功能
// plugin.js
exports.sequelize = {
enable: true,
package: 'egg-sequelize',
};
config.sequelize = {
dialect: 'mysql',
database: 'pm_webleading',
host: '127.0.0.1',
port: '3306',
username: 'pm_webleading',
password: '123456',
underscored: false,
timezone: '+08:00',
define: {
timestamps: true,
freezeTableName: true,
},
};
Mysql
MySQL 是最流行的關係型數據庫管理系統,在 WEB 應用方面 MySQL 是最好的 RDBMS(Relational Database Management System:關係數據庫管理系統)應用軟件之一
// plugin.js
exports.mysql = {
enable: true,
package: 'egg-mysql',
};
// config.default.js
config.mysql = {
client: {
// host
host: '127.0.0.1',
// 端口號
port: '3306',
// 用户名
user: 'pm_webleading',
// 密碼
password: '123456',
// 數據庫名
database: 'pm_webleading',
},
// 是否加載到 app 上,默認開啓
app: true,
// 是否加載到 agent 上,默認關閉
agent: false,
};
內置工具
已掛在egg.js,ctx對象上調用方法如下
'use strict';
const {Controller} = require('egg');
class HomeController extends Controller {
async index() {
const {ctx} = this;
try {
for (const file of ctx.request.files) {
const filePath = await ctx.helper.uploadLocaFile({file}) // cxt.helper.xxxx
}
} finally {
await ctx.cleanupRequestFiles();
}
}
}
module.exports = HomeController;
上傳文件本地
可以上傳以上配置類型的文件
uploadLocaFile({ file, filePath }) {
const { ctx } = this;
return new Promise(async (resolve, reject) => {
try {
const filename = file.filename;
const extname = path.extname(filename);
const _filePath = filePath || `public/upload/${ctx.helper.nanoid()}${extname}`;
const localPath = path.join(ctx.app.baseDir, 'app', _filePath);
// 讀取文件
const source = fs.createReadStream(file.filepath);
// 創建寫入流
const target = fs.createWriteStream(localPath);
await pump(source, target);
resolve(_filePath);
} catch (err) {
reject(err);
}
});
}
上傳圖片-帶壓縮
只能上傳圖片
uploadLocalImage({ file, filePath, width = 500, quality = 75 }) {
const { ctx } = this;
const extname = path.extname(file.filename);
const _filePath = filePath || `public/image/${ctx.helper.nanoid()}${extname}`;
const localPath = path.join(ctx.app.baseDir, 'app', _filePath);
return new Promise((resolve, reject) => {
Jimp.read(file.filepath)
.then(image => {
image.resize(width, Jimp.AUTO)
.quality(quality)
.write(localPath);
resolve(_filePath);
})
.catch(err => {
reject(err);
});
});
}
對稱加密
aesEncrypt(data, options) {
options = Object.assign({ key: this.app.config.website.key, iv: this.app.config.website.iv }, options);
let str = data;
if (typeof data === 'object') {
str = JSON.stringify(data);
}
str = CryptoJS.enc.Utf8.parse(str);
const crypto = CryptoJS.AES.encrypt(str, CryptoJS.enc.Utf8.parse(options.key), {
iv: CryptoJS.enc.Utf8.parse(options.iv),
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
});
return crypto.toString(); // 對稱加密內容
},
對稱解密
aesDecrypt(data, options) {
options = Object.assign({ key: this.app.config.website.key, iv: this.app.config.website.iv }, options);
const decrypt = CryptoJS.AES.decrypt(data, CryptoJS.enc.Utf8.parse(options.key), {
iv: CryptoJS.enc.Utf8.parse(options.iv),
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
});
return CryptoJS.enc.Utf8.stringify(decrypt); // 對稱解密內容
},
非對稱加密
encrypt(str, options) {
options = Object.assign({ publicKey: this.app.config.website.publicKey }, options);
const encrypted = new JSEncrypt();
encrypted.setPublicKey(options.publicKey.toString());
return encrypted.encrypt(str); // 非對稱加密字符串
},
非對稱解密
decrypt(str, options) {
options = Object.assign({ privateKey: this.app.config.website.privateKey }, options);
const decrypted = new JSEncrypt(); // 創建解密對象實例
decrypted.setPrivateKey(options.privateKey.toString()); // 設置私鑰
return decrypted.decrypt(str); // 非對稱解密內容
},
md5加密
md5(data) {
let str = data;
if (typeof data === 'object') {
str = JSON.stringify(data);
}
return CryptoJS.MD5(str)
.toString();
},
隨機ID
nanoid(size = 12) {
const nanoid = customAlphabet(alphabet.join(''), size);
if (size >= 12) {
return dayjs()
.format('YYYYMMDD') + nanoid(); // 獲取不重複隨機ID
}
return nanoid(); // 獲取重複隨機ID
},
jwt-生成token-校驗token
generateToken(data) {
return this.app.jwt.sign(data, this.app.config.jwt.secret); // 生成token
},
verifyToken(token) {
return this.app.jwt.verify(token, this.app.config.jwt.secret); // 驗證token
},