Koa & Mongoose & Vue實現先後端分離--13日誌系統&安全系統

上節回顧

  • 聯表查詢
  • 審批增刪改查邏輯

工做內容

  • 配置日誌系統
  • 測試日誌系統
  • 安全策略

準備工做

  • npm i -S koa-loa4 // 先切換到/server目錄下
  • npm i -S xss // 先切換到/server目錄下

日誌系統

技術選型

  • koa-loggerhtml

    • 只能設置統一的日誌
  • koa-morgannode

    • 能根據skip分類,但,可操做性太差
  • koa-log4jsgit

    • 能夠定製化
    • 週期性存儲

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 // 是否打印所屬文件名 & 行號
      }
    }
  }
}

服務端配置

代碼部分,注意轉義github

// 新建文件:/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'

}

}

};

服務端日誌工具庫

代碼部分,注意轉義npm

// 新建文件:/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)
...

測試結果

image.png

安全系統

安全系統很簡單,直接在須要進行xss攻擊處理的地方,進行一下改進便可api

var xss = require("xss");
var html = xss('<script>alert("xss");</script>');

xss包會根據預先定義好的規則,轉義標籤,過濾調具備攻擊性的屬性。安全

參考文檔

log4js-node
xss服務器

相關文章
相關標籤/搜索