lib/index.js

/**
 * @class
 * @classdesc 该类主要产生用于 router 的中间件,配置中间件不通过时的处理逻辑,本质上是通过调用 subject 完成鉴权
 */
class RBACAuth {
  /**
   * @constructor
   * @param {object} options 鉴权失败的配置参数对象
   * @param {object} options.authenticationBody 认证失败的响应体JSON
   * @param {string} options.authenticationFailedUrl 认证失败的跳转地址
   * @param {object} options.authorizationFailedBody 授权失败的响应体JSON
   * @param {number} options.authorizationFailedCode 授权失败的 HTTP 状态码
   */
  constructor (options) {
    /**
     * 认证失败的响应体JSON
     * @public
     * @type {object}
     * @default {code: 401,msg: 'unauthorized'}
     */
    this.authenticationBody = options.authenticationBody || {
      code: 401,
      msg: 'unauthorized'
    }
    /**
     * 认证失败的跳转地址
     * @public
     * @type {string}
     */
    this.authenticationFailedUrl = options.authenticationFailedUrl || '/login'
    /**
     * 授权失败的响应体JSON
     * @public
     * @type {object}
     * @default {code: 401,msg: 'unauthorized'}
     */
    this.authorizationFailedBody = options.authorizationFailedBody || {
      code: 401,
      msg: 'unauthorized'
    }
    /**
     * 授权失败的 HTTP 状态码
     * @public
     * @type {number}
     * @default 401
     */
    this.authorizationFailedCode = options.authorizationFailedCode || 401
    
  }
  /**
   * 检查当前会话的用户是否拥有指定权限
   * @example
   * rbac.checkPermissions(['admin:user'])
   * @param {array<string>} permissions 权限名称集合
   * @returns {middleWare} 中间件异步函数
   */
  checkPermissions (permissions) {
    /**
     * @callback middleWare
     * @param {object} ctx egg's ctx
     * @param {function} next call `next()` goto next stage
     */
    return async (ctx, next) => {
      const subject = await ctx.getSubject();
      if (!subject.isLogined) {
        return await this.authenticationFailed(ctx)
      }
      if (!permissions || !permissions.length) {
        await next()
      } else if (permissions.length) {
        if (subject.hasPermissions(permissions)) {
          await next()
        } else {
          await this.authorizationFailed(ctx)
        }
      }
    }
  }
  /**
   * 检查当前会话的用户是否拥有指定角色
   * @example
   * rbac.checkRoles(['admin'])
   * @param {array<string>} roleNames 角色名称集合
   * @returns {middleWare} 中间件异步函数
   */
  checkRoles (roleNames) {
    return async (ctx, next) => {
      const subject = await ctx.getSubject();
      if (!subject.isLogined) {
        return await this.authenticationFailed(ctx)
      }
      if (!roleNames || !roleNames.length) {
        await next()
      } else if (roleNames) {
        if (subject.hasRoles(roleNames)) {
          await next()
        } else {
          await this.authorizationFailed(ctx)
        }
      }
    }
  }
  /**
   * 检查当前会话的用户是否登录(认证成功与否)
   * @example
   * rbac.checkLogin()
   * @returns {middleWare} 中间件异步函数
   */
  checkLogin () {
    return async (ctx, next) => {
      const subject = await ctx.getSubject();
      if (subject.isLogined) {
        await next()
      } else {
        await this.authenticationFailed(ctx)
      }
    }
  }
  /**
   * 未授权的处理逻辑
   * @param {object} ctx egg的ctx
   */
  async authorizationFailed (ctx) {
    if (ctx.header.accept.indexOf('application/json') > -1) {
      ctx.body = this.authorizationFailedBody
    }
    ctx.status = this.authorizationFailedCode
  }
  /**
   * 未认证的处理逻辑
   * @param {object} ctx egg的ctx
   */
  async authenticationFailed (ctx) {
    if (ctx.header.accept.indexOf('application/json') > -1) {
      ctx.body = this.authenticationFailedBody
    } else {
      ctx.redirect(this.authenticationFailedUrl)
    }
  }
}

module.exports = RBACAuth