[Node.js] Node.js API permission

最近在做 for maker 的平台 makee.io,大夥會遇到如何設計 API 權限的問題,簡單來說就是某些 API 只能限定給管理者身份或是特定使用者使用,下列步驟主要就是紀錄如何利用 Node.js Express 框架加上 MongoDB 去實作這個功能

Schema design

首先第一步還是先定義使用者的 Schema (User.js),我們是使用 MongoDB 來做設計,在設計欄位的部分我們增加了一個 roles 的 key ,透過此 Key 可以去 Reference Admin & Account 兩個 Schema 去找出該 User 身份

接著利用 mongoDB 可直接寫函數 userSchema.methods.canPlayRoleOf = function(role) 來讓外部呼叫,並 response 此 User 的角色為何,Schema 如下所示:

exports = module.exports = function(app, mongoose) {  
  var userSchema = new mongoose.Schema({
    username: { type: String, unique: true },
    password: String,
    email: { type: String, unique: true },
    roles: {
      admin: { type: mongoose.Schema.Types.ObjectId, ref: 'Admin' },
      account: { type: mongoose.Schema.Types.ObjectId, ref: 'Account' }
    },
    isActive: String,
    timeCreated: { type: Date, default: Date.now },
    resetPasswordToken: String,
    resetPasswordExpires: Date,
    search: [String]
  });
  userSchema.methods.canPlayRoleOf = function(role) {
    if (role === "admin" && this.roles.admin) {
      return true;
    }

    if (role === "account" && this.roles.account) {
      return true;
    }

    return false;
  };
  userSchema.methods.defaultReturnUrl = function() {
    var returnUrl = '/';
    if (this.canPlayRoleOf('account')) {
      returnUrl = '/account/';
    }

    if (this.canPlayRoleOf('admin')) {
      returnUrl = '/admin/';
    }

    return returnUrl;
  };
  userSchema.plugin(require('./plugins/pagedFind'));
  userSchema.index({ username: 1 }, { unique: true });
  userSchema.index({ email: 1 }, { unique: true });
  userSchema.index({ timeCreated: 1 });
  userSchema.index({ search: 1 });
  userSchema.set('autoIndex', (app.get('env') === 'development'));
  app.db.model('User', userSchema);
};

URL routing 實作

Routing 觀念的主要用途是處理 URL ,所以我們利用 url 模組來取出 URL 裡的 pathname,並將 pathname 交給 route() 函數來處理 Express url routing

我們這次是想要讓一隻取得購票清單的 API 只能讓有管理者身份的使用者可以去打,針對 url routing (routes.js) 的實作程式如下所示:

  • 新增兩個 middleware 函數 ensureAuthenticated & ensureAdmin,用於判斷使用者是否登入以及是否為管理者
  • 在 app.get('/1/ticket', ensureAuthenticated) 取 ticket API,加入middleware 判斷
  • 在判斷都通過後即可取 ticket API

首先前面先加了幾個函數可以用來判斷使用者是否已經登入,接著下一隻函數用於判斷

'use strict';

function ensureAuthenticated(req, res, next) {  
  if (req.isAuthenticated()) {
    return next();
  }
  res.set('X-Auth-Required', 'true');
  req.session.returnUrl = req.originalUrl;
  res.redirect('/login/');
}

//從 user schema 函數取得使用者是否為Admin
function ensureAdmin(req, res, next) {  
  if (req.user.canPlayRoleOf('admin')) { 
    return next();
  }
  res.redirect('/');
}

function ensureAccount(req, res, next) {  
  if (req.user.canPlayRoleOf('account')) {
    if (req.app.get('require-account-verification')) {
      if (req.user.roles.account.isVerified !== 'yes' && !/^\/account\/verification\//.test(req.url)) {
        return res.redirect('/account/verification/');
      }
    }
    return next();
  }
  res.redirect('/');
}

exports = module.exports = function(app, passport) {

//middleware 判斷
  app.get('/1/ticket', ensureAuthenticated);
  app.get('/1/ticket', ensureAdmin);
//判斷都通過後即可使用此隻 API
  app.get('/1/ticket', require('./views/api/ticket').read);
};

Ben Shiue

Having being a full stack engineer. His interests in Node.js, ARM mbed, IoT solutions. Contact us : [email protected]

ALL RIGHTS RESERVED. COPYRIGHT © 2016. Designed and Coded by Makee.io