上节回顾
- 联表查询
- 审批增删改查逻辑
工作内容
- 配置日志系统
- 测试日志系统
- 安全策略
准备工作
-
npm i -S koa-loa4
// 先切换到/server
目录下 -
npm i -S xss
// 先切换到/server
目录下
日志系统
技术选型
-
koa-logger
- 只能设置统一的日志
-
koa-morgan
- 能根据
skip
分类,但,可操作性太差
- 能根据
-
koa-log4js
- 可以定制化
- 周期性存储
koa-log4js规范对象
- 参考文档
- log4默认是禁用的,不会打印日志:
log4js.configure()
- log4通过配置项开启日志功能:
log4js.configure({...})
// configure规范对象
{
// 自定义日志等级/修改内部已定义的日志等级
// 日志等级:OFF > MARK > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL
levels: {
...
},
{
// 定义日志输出类型 (参考文档:https://github.com/log4js-node/log4js-node/blob/master/docs/appenders.md)
appenders: {
error: {
// type是必选属性,其它属性配置依赖于type属性值
type: 'dateFile', // (dateFile参考文档:https://github.com/log4js-node/log4js-node/blob/master/docs/dateFile.md)
filename: path.resolve(__dirname, 'logs', 'error', 'filename'), // 定义日志存储位置,与最终指向的目录/文件同级:filename推荐不要加后缀名
pattern: '.yyyy-MM-dd.log', // 日志周期,这里加上文件后缀
alwaysIncludePattern: true, // alwaysIncludePattern为true时,才会实现以pattern规则(如:yyyy-MM-dd)新建文件一天一存
//keepFileExt: true // 没有像官网说的那样生效,所以,通过设置pattern值带后缀文件名来替代
}
},
// 预自定义日志类型:log4js.getLoggeer([category])获取该类型的日志实例
categories: {
// 必须定义一个默认类型,当没有匹配的类型时,一律按默认类型处理
default: {
},
errorLog: {
appenders: ['error'], // 指定日志输出类型
// 指定可用的最小日志等级
level: 'error', //可以使用OFF > MARK > FATAL > ERROR等级,不可以使用WARN > INFO > DEBUG > TRACE > ALL等级
enableCallStack: true // 是否打印所属文件名 & 行号
}
}
}
}
服务端配置
代码部分,注意转义
// 新建文件:/server/config/log.js
const path = require('path');
const { checkDirExist } = require('../utils/dir');
// 日志根目录
const baseLogDir = path.resolve(\_\_dirname, '../logs');
// 错误日志
const errorDir = 'error';
const errorFileName = 'error';
const errorDirPath = path.resolve(baseLogDir, errorDir);
const errorLogPath = path.resolve(errorDirPath, errorFileName);
// 访问日志
const accessDir = 'access';
const accessFileName = 'access';
const accessDirPath = path.resolve(baseLogDir, accessDir);
const accessLogPath = path.resolve(accessDirPath, accessFileName);
// 响应日志
const responseDir = 'response';
const responseFileName = 'response';
const responseDirPath = path.resolve(baseLogDir, responseDir);
const responseLogPath = path.resolve(responseDirPath, responseFileName);
\[errorDirPath, accessDirPath, responseDirPath\].forEach(p \=> {
checkDirExist(p);
})
module.exports = {
// 定义日志输出类型https://github.com/log4js-node/log4js-node/blob/master/docs/appenders.md
appenders: {
console: {
type: 'stdout'
},
error: {
type: 'dateFile', //https://github.com/log4js-node/log4js-node/blob/master/docs/dateFile.md
filename: errorLogPath, // 定义生成文件的路径。
daysToKeep: 7, // 保存7天日志,大于7天的,删除;
pattern: '.yyyy-MM-dd.log',
alwaysIncludePattern: true, // 只有该属性设置为true,才会以pattern追加重命名filename
// keepFileExt: true //不起作用
},
access: {
type: 'dateFile',
filename: accessLogPath,
daysToKeep: 7, // 保存7天日志,大于7天的,删除;
pattern: '.yyyy-MM-dd.log',
alwaysIncludePattern: true, // 只有该属性设置为true,才会以pattern追加重命名filename
// keepFileExt: true //不起作用
},
response: {
type: 'dateFile',
filename: responseLogPath,
daysToKeep: 7, // 保存7天日志,大于7天的,删除;
pattern: '\-yyyy-MM-dd.log',
alwaysIncludePattern: true, // 只有该属性设置为true,才会以pattern追加重命名filename
// keepFileExt: true //不起作用
}
},
// 定义Logger对象类型,用于log4js.getLogger(\[category\])
categories: {
default: {
appenders: \['console'\], level: 'all'
},
errorLogger: {
appenders: \['error'\], level: 'error'
},
accessLogger: {
appenders: \['access'\], level: 'info'
},
responseLogger: {
appenders: \['response'\], level: 'info'
}
}
};
服务端日志工具库
代码部分,注意转义
// 新建文件:/server/utils/log.js
const log4js = require('koa-log4');
const config = require('../config/log');
//以规范对象开启日志功能
log4js.configure(config);
// 获取日志对象实例
const errorLogger = log4js.getLogger('errorLogger');
const accessLogger = log4js.getLogger('accessLogger');
const responseLogger = log4js.getLogger('responseLogger');
const consoleLogger = log4js.getLogger();
const logUtil = {};
// 封装错误日志
logUtil.logError \= function (ctx, error, resTime) {
if (ctx && error) {
errorLogger.error(formatError(ctx, error, resTime));
}
};
// 封装请求日志
logUtil.logAccess \= function (ctx, resTime) {
if (ctx) {
accessLogger.info(formatAccessLog(ctx, resTime));
}
};
// 封装响应日志
logUtil.logResponse \= function (ctx, resTime) {
if (ctx) {
responseLogger.info(formatRes(ctx, resTime));
}
};
logUtil.logInfo \= function (info) {
if (info) {
consoleLogger.info(formatInfo(info));
}
};
const formatInfo \= function (info) {
let logText = '';
// 响应日志开始
logText += '\\n' + '\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* console log start \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*' + '\\n';
// 响应内容
logText += 'console detail: ' + '\\n' + JSON.stringify(info) + '\\n';
// 响应日志结束
logText += '\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* console log end \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*' + '\\n';
return logText;
};
// 格式化响应日志
const formatRes \= function (ctx, resTime) {
let logText = '';
// 响应日志开始
logText += '\\n' + '\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* response log start \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*' + '\\n';
// 添加请求日志
logText += formatAccessLog(ctx, resTime);
// 响应状态码
logText += '\\n' + 'response status: ' + ctx.status + '\\n';
// 响应内容
logText += 'response body: ' + '\\n' + JSON.stringify(ctx.body) + '\\n';
// 响应日志结束
logText += '\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* response log end \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*' + '\\n';
return logText;
};
// 格式化错误日志
const formatError \= function (ctx, err, resTime) {
let logText = '';
// 错误信息开始
logText += '\\n' + '\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* error log start \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*' + '\\n';
// 添加请求日志
logText += formatAccessLog(ctx, resTime);
// 错误名称
logText += '\\n' + 'err name: ' + err.name + '\\n';
// 错误信息
logText += 'err message: ' + err.message + '\\n';
// 错误详情
logText += 'err stack: ' + err.stack + '\\n';
// 错误信息结束
logText += '\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* error log end \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*' + '\\n';
return logText;
};
// 格式化请求日志
const formatAccessLog \= function (ctx, resTime) {
const { method, originalUrl, ip, query, params, body } = ctx.request;
let logText = '';
// 客户端ip
logText += 'request client ip: ' + ip + '\\n';
// 客户端
logText += 'request userAgent: ' + ctx.header\['user-agent'\] + '\\n';
// 访问协议
logText += 'request protocol: ' + ctx.protocol + '\\n';
// 访问方法
logText += 'request method: ' + method + '\\n';
// 请求原始地址
logText += 'request originalUrl: ' + originalUrl + '\\n';
// 请求参数
logText += params ? 'request params: ' + JSON.stringify(params) + '\\n' : '';
logText += query ? 'request query: ' + JSON.stringify(query) + '\\n' : '';
logText += body ? 'request body: ' + JSON.stringify(body) + '\\n' : '';
// 服务器响应时间
logText += 'response time: ' + resTime + '\\n';
return logText;
};
module.exports = logUtil;
服务端使用日志服务
// 更新文件:在需要使用的地方或统一拦截的地方修改
// 如:/server/app.js
...
const logUtil = require('./utils/log');
...
// 错误处理
app.use(function(ctx, next){
return next().catch((err) => {
logUtil.logError(ctx, err)
...
测试结果
安全系统
安全系统很简单,直接在需要进行xss
攻击处理的地方,进行一下改进即可
var xss = require("xss");
var html = xss('<script>alert("xss");</script>');
xss
包会根据预先定义好的规则,转义标签,过滤调具有攻击性的属性。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。