Node.js + MongoDB рдкрд░ RESTful API

рдПрдХ рдореЛрдмрд╛рдЗрд▓ рдРрдк рдбреЗрд╡рд▓рдкрд░ рдХреЗ рд░реВрдк рдореЗрдВ, рдореБрдЭреЗ рдЕрдХреНрд╕рд░ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдбреЗрдЯрд╛, рдкреНрд░рд╛рдзрд┐рдХрд░рдг, рдФрд░ рдмрд╣реБрдд рдХреБрдЫ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдмреИрдХрдПрдВрдб рд╕реЗрд╡рд╛рдУрдВ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред рдмреЗрд╢рдХ, рдРрд╕реЗ рдХрд╛рд░реНрдпреЛрдВ рдХреЗ рд▓рд┐рдП рдЖрдк рдмреАрдПрдПрдПрд╕ (рдкрд╛рд░реНрд╕, рдмреИрдХрдПрдВрдбрд▓реЗрд╕, рдЖрджрд┐ ...) рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рд▓реЗрдХрд┐рди рдЖрдкрдХрд╛ рдирд┐рд░реНрдгрдп рд╣рдореЗрд╢рд╛ рдЕрдзрд┐рдХ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рдФрд░ рд╡реНрдпрд╛рд╡рд╣рд╛рд░рд┐рдХ рд╣реЛрддрд╛ рд╣реИред

рдФрд░ рдлрд┐рд░ рднреА рдореИрдВрдиреЗ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдЕрдЬреНрдЮрд╛рдд рдХреЗ рд▓рд┐рдП рдкреНрд░реМрджреНрдпреЛрдЧрд┐рдХрд┐рдпреЛрдВ рдХрд╛ рдЕрдзреНрдпрдпрди рдХрд░рдиреЗ рдХрд╛ рдлреИрд╕рд▓рд╛ рдХрд┐рдпрд╛, рдЬреЛ рдЕрдм рдмрд╣реБрдд рд▓реЛрдХрдкреНрд░рд┐рдп рд╣реИрдВ рдФрд░ рд╢реБрд░реБрдЖрддреА рд▓реЛрдЧреЛрдВ рджреНрд╡рд╛рд░рд╛ рдЖрд╕рд╛рдиреА рд╕реЗ рдорд╣рд╛рд░рдд рд╣рд╛рд╕рд┐рд▓ рдХрд░ рд▓реЗрддреЗ рд╣реИрдВ рдФрд░ рдмрдбрд╝реЗ рдкреИрдорд╛рдиреЗ рдкрд░ рдкрд░рд┐рдпреЛрдЬрдирд╛рдУрдВ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЧрд╣рди рдЬреНрдЮрд╛рди рдФрд░ рдЕрдиреБрднрд╡ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реЛрддреА рд╣реИред рддреЛ рдЪрд▓реЛ рдПрдХ рд╕рд╛рде рдЬрд╛рдВрдЪреЗрдВ рдХрд┐ рдХреНрдпрд╛ рдПрдХ рдЖрдо рдЖрджрдореА рдЕрдкрдиреЗ рдкреНрд░рднрд╛рд╡реА рдФрд░ рд╕рд╣реА рдмреИрдХрдПрдВрдб рд▓рд┐рдЦ рд╕рдХрддрд╛ рд╣реИред

рдпрд╣ рд▓реЗрдЦ MongoDB рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП Express.js рдлреНрд░реЗрдорд╡рд░реНрдХ рдФрд░ Mongoose.js рдореЙрдбреНрдпреВрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ Node.js рдкрд░ рдПрдХ рдореЛрдмрд╛рдЗрд▓ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЗ рд▓рд┐рдП REST API рдХреЗ рдирд┐рд░реНрдорд╛рдг рдкрд░ рдЪрд░реНрдЪрд╛ рдХрд░реЗрдЧрд╛ред рдкрд╣реБрдВрдЪ рдХреЛ рдирд┐рдпрдВрддреНрд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдо OAuth2orize рдФрд░ Passport.js рдореЙрдбреНрдпреВрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ OAuth 2.0 рддрдХрдиреАрдХ рдХрд╛ рд╕рд╣рд╛рд░рд╛ рд▓реЗрдВрдЧреЗред

рдореИрдВ рдПрдХ рдирд┐рд░рдкреЗрдХреНрд╖ рд╢реБрд░реБрдЖрдд рдХреЗ рдкрд░рд┐рдкреНрд░реЗрдХреНрд╖реНрдп рд╕реЗ рд▓рд┐рдЦ рд░рд╣рд╛ рд╣реВрдВред рдХреЛрдб рдФрд░ рддрд░реНрдХ рдкрд░ рдХрд┐рд╕реА рднреА рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдФрд░ рд╕реБрдзрд╛рд░ рдкрд░ рдЦреБрд╢реА!

рд╕рд╛рдордЧреНрд░реА

  1. Node.js + Express.js, рдПрдХ рд╕рд╛рдзрд╛рд░рдг рд╡реЗрдм рд╕рд░реНрд╡рд░
  2. рд╣реИрдВрдбрд▓рд┐рдВрдЧ рдореЗрдВ рддреНрд░реБрдЯрд┐
  3. рдкреНрд░рддрд┐рд╖реНрдард┐рдд рдПрдкреАрдЖрдИ рд╕рдорд╛рдкрди рдмрд┐рдВрджреБ, CRUD
  4. MongoDB рдФрд░ Mongoose.js
  5. рдЕрднрд┐рдЧрдо рдирд┐рдпрдВрддреНрд░рдг - OAuth 2.0, Passport.js


рдореИрдВ OSX, IDE - JetBrains WebStorm рдореЗрдВ рдХрд╛рдо рдХрд░рддрд╛ рд╣реВрдВ ред

рдореИрдВрдиреЗ рдЗрд▓рд┐рдпрд╛ рдХреИрдВрдЯрд░ рдХреЗ рд╕реНрдХреНрд░реИрдирд╛рд╕реНрдЯ рдореЗрдВ Node.js рдХреА рдореВрд▓ рдмрд╛рддреЗрдВ рд╕реАрдЦреАрдВ, рдореИрдВ рдЗрд╕рдХреА рдЕрддреНрдпрдзрд┐рдХ рдЕрдиреБрд╢рдВрд╕рд╛ рдХрд░рддрд╛ рд╣реВрдВ! ( рдФрд░ рдпрд╣рд╛рдВ рд╣рдм рдкрд░ рдЙрдирдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдПрдХ рдкреЛрд╕реНрдЯ рд╣реИ )

рдЕрдВрддрд┐рдо рдЪрд░рдг рдореЗрдВ рддреИрдпрд╛рд░ рдкрд░рд┐рдпреЛрдЬрдирд╛ GitHub рдкрд░ рд▓реА рдЬрд╛ рд╕рдХрддреА рд╣реИ ред рд╕рднреА рдореЙрдбреНрдпреВрд▓ рдХреЛ рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдлрд╝реЛрд▓реНрдбрд░ рдореЗрдВ npm рдЗрдВрд╕реНрдЯреЙрд▓ рдХрдорд╛рдВрдб рдЪрд▓рд╛рдПрдВред

1. Node.js + Express.js, рдПрдХ рд╕рд╛рдзрд╛рд░рдг рд╡реЗрдм рд╕рд░реНрд╡рд░


Node.js рдореЗрдВ рдЧреИрд░-рдЕрд╡рд░реЛрдзрдХ I / O рд╣реИ, рдЬреЛ рдПрдХ рдПрдкреАрдЖрдИ рдХреЗ рд▓рд┐рдП рдЕрдЪреНрдЫрд╛ рд╣реИ рдЬреЛ рдХрдИ рдХреНрд▓рд╛рдЗрдВрдЯ рдПрдХреНрд╕реЗрд╕ рдХрд░реЗрдЧрд╛ред Express.js рдПрдХ рд╡рд┐рдХрд╕рд┐рдд, рд╣рд▓реНрдХрд╛ рдврд╛рдВрдЪрд╛ рд╣реИ рдЬреЛ рдЖрдкрдХреЛ рдЙрди рд╕рднреА рд░рд╛рд╕реНрддреЛрдВ (рдПрдкреАрдЖрдИ рд╕рдорд╛рдкрди рдмрд┐рдВрджреБ) рдХрд╛ рд╡рд░реНрдгрди рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ рдЬрд┐рдиреНрд╣реЗрдВ рд╣рдо рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░реЗрдВрдЧреЗред рдЖрдк рдЗрд╕рдХреЗ рд▓рд┐рдП рдХрдИ рдЙрдкрдпреЛрдЧреА рдореЙрдбреНрдпреВрд▓ рднреА рдкрд╛ рд╕рдХрддреЗ рд╣реИрдВред

рдПрдХ рдПрдХрд▓ server.js рдлрд╝рд╛рдЗрд▓ рдХреЗ рд╕рд╛рде рдПрдХ рдирдИ рдкрд░рд┐рдпреЛрдЬрдирд╛ рдмрдирд╛рдПрдБред рдЪреВрдВрдХрд┐ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдкреВрд░реА рддрд░рд╣ рд╕реЗ Express.js рдкрд░ рдирд┐рд░реНрднрд░ рдХрд░реЗрдЧрд╛, рдЗрд╕реЗ рдЗрдВрд╕реНрдЯреЙрд▓ рдХрд░реЗрдВред рдерд░реНрдб-рдкрд╛рд░реНрдЯреА рдореЙрдбреНрдпреВрд▓ рдиреЛрдб рдлрд╝реЛрд▓реНрдбрд░ рдореЗрдВ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдлрд╝реЛрд▓реНрдбрд░ рдореЗрдВ npm install modulename ред

 cd NodeAPI npm i express 


Express рдиреЛрдб_рдореЙрдбрд▓ рдлрд╝реЛрд▓реНрдбрд░ рдореЗрдВ рд╕реНрдерд╛рдкрд┐рдд рд╣реИред рдЗрд╕реЗ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рд╕реЗ рдХрдиреЗрдХреНрдЯ рдХрд░реЗрдВ:

 var express = require('express'); var app = express(); app.listen(1337, function(){ console.log('Express server listening on port 1337'); }); 


IDE рдпрд╛ рдХрдВрд╕реЛрд▓ ( node server.js ) рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдЪрд▓рд╛рдПрдБред рдпрд╣ рдХреЛрдб рд▓реЛрдХрд▓рд╣реЛрд╕реНрдЯ: 1337 рдкрд░ рдПрдХ рд╡реЗрдм рд╕рд░реНрд╡рд░ рдмрдирд╛рдПрдЧрд╛ред рдпрджрд┐ рдЖрдк рдЗрд╕реЗ рдЦреЛрд▓рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рдпрд╣ рд╕рдВрджреЗрд╢ Cannot GET / ред рдРрд╕рд╛ рдЗрд╕рд▓рд┐рдП рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рд╣рдордиреЗ рдЕрднреА рддрдХ рдПрдХ рднреА рдорд╛рд░реНрдЧ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдирд╣реАрдВ рдХрд┐рдпрд╛ рд╣реИред рдЕрдЧрд▓рд╛, рдХрдИ рдкрде рдмрдирд╛рдПрдВ рдФрд░ рдореВрд▓ рдПрдХреНрд╕рдкреНрд░реЗрд╕ рд╕реЗрдЯрд┐рдВрдЧреНрд╕ рдмрдирд╛рдПрдВред

 var express = require('express'); var path = require('path'); //     var app = express(); app.use(express.favicon()); //   ,      app.use(express.logger('dev')); //        app.use(express.bodyParser()); //  ,   JSON   app.use(express.methodOverride()); //  put  delete app.use(app.router); //       app.use(express.static(path.join(__dirname, "public"))); //    ,     public/ (    index.html) app.get('/api', function (req, res) { res.send('API is running'); }); app.listen(1337, function(){ console.log('Express server listening on port 1337'); }); 


рдЕрдм рд▓реЛрдХрд▓рд╣реЛрд╕реНрдЯ: 1337 / рдПрдкреА рд╣рдорд╛рд░рд╛ рд╕рдВрджреЗрд╢ рд▓реМрдЯрд╛ рджреЗрдЧрд╛ред localhost: 1337 index.html рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░реЗрдЧрд╛ред

рдпрд╣рд╛рдБ рд╣рдо рдПрд░рд░ рд╣реИрдВрдбрд▓рд┐рдВрдЧ рдкрд░ рдЖрдЧреЗ рдмрдврд╝рддреЗ рд╣реИрдВред

2. рддреНрд░реБрдЯрд┐ рд╕реЗ рдирд┐рдкрдЯрдиреЗ


рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рд╡рд┐рдВрд╕реНрдЯрди рд▓реЙрдЧрд┐рдВрдЧ рдореЙрдбреНрдпреВрд▓ рдХреЛ рдХрдиреЗрдХреНрдЯ рдХрд░реЗрдВред рд╣рдо рдЕрдкрдиреЗ рдЖрд╡рд░рдг рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗред npm i winston рдХреЛ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рд░реВрдЯ рдореЗрдВ рд╕реНрдерд╛рдкрд┐рдд рдХрд░реЗрдВ, рдлрд┐рд░ рд╡рд╣рд╛рдВ libs / folder рдФрд░ log.js. рдлрд╛рдЗрд▓ рдмрдирд╛рдПрдВ

 var winston = require('winston'); function getLogger(module) { var path = module.filename.split('/').slice(-2).join('/'); //    ,    return new winston.Logger({ transports : [ new winston.transports.Console({ colorize: true, level: 'debug', label: path }) ] }); } module.exports = getLogger; 


рд╣рдордиреЗ рд▓реЙрдЧ рдХреЗ рд▓рд┐рдП 1 рдкрд░рд┐рд╡рд╣рди рдмрдирд╛рдпрд╛ - рдХрдВрд╕реЛрд▓ рдХреЛред рд╣рдо рд╕рдВрджреЗрд╢реЛрдВ рдХреЛ рдЕрд▓рдЧ рд╕реЗ рднреА рд╕реЙрд░реНрдЯ рдФрд░ рд╕рд╣реЗрдЬ рд╕рдХрддреЗ рд╣реИрдВ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдбреЗрдЯрд╛рдмреЗрд╕ рдпрд╛ рдлрд╝рд╛рдЗрд▓ рдореЗрдВред рд▓рдХрдбрд╝рд╣рд╛рд░реЗ рдХреЛ рд╣рдорд╛рд░реЗ server.js рд╕реЗ рдХрдиреЗрдХреНрдЯ рдХрд░реЗрдВред

 var express = require('express'); var path = require('path'); //     var log = require('./libs/log')(module); var app = express(); app.use(express.favicon()); //   ,      app.use(express.logger('dev')); //        app.use(express.bodyParser()); //  ,   JSON   app.use(express.methodOverride()); //  put  delete app.use(app.router); //       app.use(express.static(path.join(__dirname, "public"))); //    ,     public/ (    index.html) app.get('/api', function (req, res) { res.send('API is running'); }); app.listen(1337, function(){ log.info('Express server listening on port 1337'); }); 


рд╣рдорд╛рд░рд╛ рдиреНрдпреВрдЬрд╝рд▓реЗрдЯрд░ рдЕрдм рдХрдВрд╕реЛрд▓ рдореЗрдВ рдЕрд▓рдЧ рд╕реЗ рдЦреВрдмрд╕реВрд░рддреА рд╕реЗ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред 404 рдФрд░ 500 рдХреЛ рд╕рдВрднрд╛рд▓рдиреЗ рдореЗрдВ рддреНрд░реБрдЯрд┐ рдЬреЛрдбрд╝реЗрдВред

 app.use(function(req, res, next){ res.status(404); log.debug('Not found URL: %s',req.url); res.send({ error: 'Not found' }); return; }); app.use(function(err, req, res, next){ res.status(err.status || 500); log.error('Internal error(%d): %s',res.statusCode,err.message); res.send({ error: err.message }); return; }); app.get('/ErrorExample', function(req, res, next){ next(new Error('Random error!')); }); 


рдЕрдм, рдпрджрд┐ рд░рд╛рд╕реНрддреЗ рдЙрдкрд▓рдмреНрдз рдирд╣реАрдВ рд╣реИрдВ, рддреЛ рдПрдХреНрд╕рдкреНрд░реЗрд╕ рд╣рдорд╛рд░реЗ рд╕рдВрджреЗрд╢ рдХреЛ рд╡рд╛рдкрд╕ рдХрд░ рджреЗрдЧрд╛ред рдПрдХ рдЖрдВрддрд░рд┐рдХ рдЕрдиреБрдкреНрд░рдпреЛрдЧ рддреНрд░реБрдЯрд┐ рдХреЗ рдорд╛рдорд▓реЗ рдореЗрдВ, рд╣рдорд╛рд░рд╛ рд╣реИрдВрдбрд▓рд░ рднреА рдХрд╛рдо рдХрд░реЗрдЧрд╛, рдЗрд╕реЗ рд▓реЛрдХрд▓рд╣реЛрд╕реНрдЯ: 1337 / ErrorExample рд╕реЗ рд╕рдВрдкрд░реНрдХ рдХрд░рдХреЗ рджреЗрдЦрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред

3. RESTful API рдПрдВрдбрдкреЙрдЗрдВрдЯ, CRUD


рдХреБрдЫ "рд▓реЗрдЦ" (рд▓реЗрдЦ) рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рдХреЗ рд▓рд┐рдП рд░рд╛рд╕реНрддреЗ рдЬреЛрдбрд╝реЗрдВред рдПрдХ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рдПрдкреАрдЖрдИ рдХреЛ рд╕рд╣реА рддрд░реАрдХреЗ рд╕реЗ рдмрдирд╛рдиреЗ рдХреЗ рддрд░реАрдХреЗ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрддрд╛рддреЗ рд╣реБрдП рд╣рдм рдкрд░ рдПрдХ рдЕрджреНрднреБрдд рд▓реЗрдЦ рд╣реИред рдЬрдмрдХрд┐ рд╣рдо рдЙрдиреНрд╣реЗрдВ рддрд░реНрдХ рд╕реЗ рдирд╣реАрдВ рднрд░реЗрдВрдЧреЗ, рд╣рдо рдЗрд╕реЗ рдЕрдЧрд▓реЗ рдЪрд░рдг рдореЗрдВ рдХрд░реЗрдВрдЧреЗ, рдбреЗрдЯрд╛рдмреЗрд╕ рдХреЗ рд╕рд╛рде рдЬреБрдбрд╝рд╛ рд╣реБрдЖ рд╣реИред

 app.get('/api/articles', function(req, res) { res.send('This is not implemented now'); }); app.post('/api/articles', function(req, res) { res.send('This is not implemented now'); }); app.get('/api/articles/:id', function(req, res) { res.send('This is not implemented now'); }); app.put('/api/articles/:id', function (req, res){ res.send('This is not implemented now'); }); app.delete('/api/articles/:id', function (req, res){ res.send('This is not implemented now'); }); 


рдкреЛрд╕реНрдЯ / рдкреБрдЯ / рдбрд┐рд▓реАрдЯ рдХреЗ рдкрд░реАрдХреНрд╖рдг рдХреЗ рд▓рд┐рдП, рдореИрдВ cURL - httpie рдкрд░ рдПрдХ рдЕрджреНрднреБрдд рдЖрд╡рд░рдг рдХреА рд╕рд┐рдлрд╛рд░рд┐рд╢ рдХрд░реВрдВрдЧрд╛ ред рдЖрдЧреЗ рдореИрдВ рдЗрд╕ рдЯреВрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдкреНрд░рд╢реНрдиреЛрдВ рдХреЗ рдЙрджрд╛рд╣рд░рдг рджреВрдВрдЧрд╛ред

4. MongoDB рдФрд░ Mongoose.js


рдПрдХ DBMS рдХрд╛ рдЪрдпрди, рдореИрдВ рдлрд┐рд░ рд╕реЗ рдХреБрдЫ рдирдпрд╛ рд╕реАрдЦрдиреЗ рдХреА рдЗрдЪреНрдЫрд╛ рджреНрд╡рд╛рд░рд╛ рдирд┐рд░реНрджреЗрд╢рд┐рдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ред MongoDB рд╕рдмрд╕реЗ рд▓реЛрдХрдкреНрд░рд┐рдп NoSQL рджрд╕реНрддрд╛рд╡реЗрдЬрд╝-рдЙрдиреНрдореБрдЦ DBMS рд╣реИред Mongoose.js - рдПрдХ рдЖрд╡рд░рдг рдЬреЛ рдЖрдкрдХреЛ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рдФрд░ рдХрд╛рд░реНрдпрд╛рддреНрдордХ рджрд╕реНрддрд╛рд╡реЗрдЬрд╝ рдпреЛрдЬрдирд╛рдПрдВ рдмрдирд╛рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред

рдбрд╛рдЙрдирд▓реЛрдб рдХрд░реЗрдВ рдФрд░ MongoDB рд╕реНрдерд╛рдкрд┐рдд рдХрд░реЗрдВред npm i mongoose рд╕реНрдерд╛рдкрд┐рдд рдХрд░реЗрдВ: npm i mongoose ред рдореИрдВрдиреЗ рдбреЗрдЯрд╛рдмреЗрд╕ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХреЛ рд▓рд┐рдмрд╛рд╕ / рдореЛрдВрдЧреЛрдЬрд╝реЛ.рдЬреЗрдПрд╕ рдлрд╛рдЗрд▓ рдореЗрдВ рдЪреБрдирд╛ рд╣реИред

 var mongoose = require('mongoose'); var log = require('./log')(module); mongoose.connect('mongodb://localhost/test1'); var db = mongoose.connection; db.on('error', function (err) { log.error('connection error:', err.message); }); db.once('open', function callback () { log.info("Connected to DB!"); }); var Schema = mongoose.Schema; // Schemas var Images = new Schema({ kind: { type: String, enum: ['thumbnail', 'detail'], required: true }, url: { type: String, required: true } }); var Article = new Schema({ title: { type: String, required: true }, author: { type: String, required: true }, description: { type: String, required: true }, images: [Images], modified: { type: Date, default: Date.now } }); // validation Article.path('title').validate(function (v) { return v.length > 5 && v.length < 70; }); var ArticleModel = mongoose.model('Article', Article); module.exports.ArticleModel = ArticleModel; 


рдЗрд╕ рдлрд╝рд╛рдЗрд▓ рдореЗрдВ, рдбреЗрдЯрд╛рдмреЗрд╕ рд╕реЗ рдПрдХ рдХрдиреЗрдХреНрд╢рди рдмрдирд╛рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдФрд░ рдСрдмреНрдЬреЗрдХреНрдЯ рд╕реНрдХреАрдо рднреА рдШреЛрд╖рд┐рдд рдХреА рдЬрд╛рддреА рд╣реИрдВред рд▓реЗрдЦ рдореЗрдВ рдЪрд┐рддреНрд░ рд╡рд╕реНрддреБрдПрдВ рд╣реЛрдВрдЧреАред рдпрд╣рд╛рдВ рд╡рд┐рднрд┐рдиреНрди рдкреНрд░рдХрд╛рд░ рдХреЗ рдЬрдЯрд┐рд▓ рдорд╛рдиреНрдпрддрд╛рдУрдВ рдХрд╛ рднреА рд╡рд░реНрдгрди рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред

рдЗрд╕ рд╕реНрддрд░ рдкрд░, рдореЗрд░рд╛ рд╕реБрдЭрд╛рд╡ рд╣реИ рдХрд┐ рдЗрд╕рдореЗрдВ рдбреЗрдЯрд╛рдмреЗрд╕ рдХреЗ рд▓рд┐рдП рдкрде рдХреЛ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП nconf рдореЙрдбреНрдпреВрд▓ рдХреЛ рдЬреЛрдбрд╝рдирд╛ ред рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдореЗрдВ рднреА, рдЙрд╕ рдкреЛрд░реНрдЯ рдХреЛ рд╕рд╣реЗрдЬреЗрдВ рдЬрд┐рд╕ рдкрд░ рд╕рд░реНрд╡рд░ рдмрдирд╛рдпрд╛ рдЧрдпрд╛ рд╣реИред рдореЙрдбреНрдпреВрд▓ npm i nconf рд╕рд╛рде рд╕реНрдерд╛рдкрд┐рдд рд╣реИред рдЖрд╡рд░рдг рд▓рд┐рдм / config.js рд╣реЛрдЧрд╛

 var nconf = require('nconf'); nconf.argv() .env() .file({ file: './config.json' }); module.exports = nconf; 


рдпрд╣ рдЗрд╕ рдкреНрд░рдХрд╛рд░ рд╣реИ рдХрд┐ рд╣рдореЗрдВ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдХреЗ рд░реВрдЯ рдореЗрдВ config.json рдмрдирд╛рдирд╛ рд╣реЛрдЧрд╛ред

 { "port" : 1337, "mongoose": { "uri": "mongodb://localhost/test1" } } 


Mongoose.js рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрди (рдХреЗрд╡рд▓ рд╣реЗрдбрд░ рдореЗрдВ):

 var config = require('./config'); mongoose.connect(config.get('mongoose:uri')); 


Server.js рдкрд░рд┐рд╡рд░реНрддрди:

 var config = require('./libs/config'); app.listen(config.get('port'), function(){ log.info('Express server listening on port ' + config.get('port')); }); 


рдЕрдм рд╣рдорд╛рд░реЗ рдореМрдЬреВрджрд╛ рд░рд╛рд╕реНрддреЛрдВ рдореЗрдВ CRUD рдХрд╛рд░реНрд░рд╡рд╛рдИ рдЬреЛрдбрд╝реЗрдВред

 var log = require('./libs/log')(module); var ArticleModel = require('./libs/mongoose').ArticleModel; app.get('/api/articles', function(req, res) { return ArticleModel.find(function (err, articles) { if (!err) { return res.send(articles); } else { res.statusCode = 500; log.error('Internal error(%d): %s',res.statusCode,err.message); return res.send({ error: 'Server error' }); } }); }); app.post('/api/articles', function(req, res) { var article = new ArticleModel({ title: req.body.title, author: req.body.author, description: req.body.description, images: req.body.images }); article.save(function (err) { if (!err) { log.info("article created"); return res.send({ status: 'OK', article:article }); } else { console.log(err); if(err.name == 'ValidationError') { res.statusCode = 400; res.send({ error: 'Validation error' }); } else { res.statusCode = 500; res.send({ error: 'Server error' }); } log.error('Internal error(%d): %s',res.statusCode,err.message); } }); }); app.get('/api/articles/:id', function(req, res) { return ArticleModel.findById(req.params.id, function (err, article) { if(!article) { res.statusCode = 404; return res.send({ error: 'Not found' }); } if (!err) { return res.send({ status: 'OK', article:article }); } else { res.statusCode = 500; log.error('Internal error(%d): %s',res.statusCode,err.message); return res.send({ error: 'Server error' }); } }); }); app.put('/api/articles/:id', function (req, res){ return ArticleModel.findById(req.params.id, function (err, article) { if(!article) { res.statusCode = 404; return res.send({ error: 'Not found' }); } article.title = req.body.title; article.description = req.body.description; article.author = req.body.author; article.images = req.body.images; return article.save(function (err) { if (!err) { log.info("article updated"); return res.send({ status: 'OK', article:article }); } else { if(err.name == 'ValidationError') { res.statusCode = 400; res.send({ error: 'Validation error' }); } else { res.statusCode = 500; res.send({ error: 'Server error' }); } log.error('Internal error(%d): %s',res.statusCode,err.message); } }); }); }); app.delete('/api/articles/:id', function (req, res){ return ArticleModel.findById(req.params.id, function (err, article) { if(!article) { res.statusCode = 404; return res.send({ error: 'Not found' }); } return article.remove(function (err) { if (!err) { log.info("article removed"); return res.send({ status: 'OK' }); } else { res.statusCode = 500; log.error('Internal error(%d): %s',res.statusCode,err.message); return res.send({ error: 'Server error' }); } }); }); }); 


рдорд╛рдирдЧреЛ рдФрд░ рд╡рд░реНрдгрд┐рдд рдпреЛрдЬрдирд╛рдУрдВ рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рдж - рд╕рднреА рдСрдкрд░реЗрд╢рди рдмреЗрд╣рдж рд╕реНрдкрд╖реНрдЯ рд╣реИрдВред рдЕрдм, рдиреЛрдб.рдЬреЗрдПрд╕ рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдЖрдкрдХреЛ mongod рдХрдорд╛рдВрдб рд╢реБрд░реВ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдПред mongo - рдбреЗрдЯрд╛рдмреЗрд╕ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдЙрдкрдпреЛрдЧрд┐рддрд╛, рд╕реЗрд╡рд╛ рд╣реА mongod ред рдЖрдкрдХреЛ рдбреЗрдЯрд╛рдмреЗрд╕ рдореЗрдВ рдкрд╣рд▓реЗ рд╕реЗ рдХреБрдЫ рднреА рдмрдирд╛рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИред

Httpie рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдирдореВрдирд╛ рдЕрдиреБрд░реЛрдз:

 http POST http://localhost:1337/api/articles title=TestArticle author='John Doe' description='lorem ipsum dolar sit amet' images:='[{"kind":"thumbnail", "url":"http://habrahabr.ru/images/write-topic.png"}, {"kind":"detail", "url":"http://habrahabr.ru/images/write-topic.png"}]' http http://localhost:1337/api/articles http http://localhost:1337/api/articles/52306b6a0df1064e9d000003 http PUT http://localhost:1337/api/articles/52306b6a0df1064e9d000003 title=TestArticle2 author='John Doe' description='lorem ipsum dolar sit amet' images:='[{"kind":"thumbnail", "url":"http://habrahabr.ru/images/write-topic.png"}, {"kind":"detail", "url":"http://habrahabr.ru/images/write-topic.png"}]' http DELETE http://localhost:1337/api/articles/52306b6a0df1064e9d000003 


рдЗрд╕ рд╕реНрддрд░ рдкрд░ рдкрд░рд┐рдпреЛрдЬрдирд╛ GitHub рдкрд░ рдПрдХ рдирдЬрд╝рд░ рдбрд╛рд▓ рд╕рдХрддреА рд╣реИ ред

5. рдЕрднрд┐рдЧрдо рдирд┐рдпрдВрддреНрд░рдг - OAuth 2.0, Passport.js


рдкрд╣реБрдВрдЪ рдХреЛ рдирд┐рдпрдВрддреНрд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдореИрдВ OAuth 2 рдХрд╛ рд╕рд╣рд╛рд░рд╛ рд▓реВрдВрдЧрд╛ред рд╢рд╛рдпрдж рдпрд╣ рдмреЗрдорд╛рдиреА рд╣реИ, рд▓реЗрдХрд┐рди рднрд╡рд┐рд╖реНрдп рдореЗрдВ рдпрд╣ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдЕрдиреНрдп OAuth рдкреНрд░рджрд╛рддрд╛рдУрдВ рдХреЗ рд╕рд╛рде рдПрдХреАрдХрд░рдг рдХреА рд╕реБрд╡рд┐рдзрд╛ рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдореБрдЭреЗ Node.js. рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛-рдкрд╛рд╕рд╡рд░реНрдб OAuth2 рдкреНрд░рд╡рд╛рд╣ рдХреЗ рдХрд╛рдо рдХреЗ рдЙрджрд╛рд╣рд░рдг рдирд╣реАрдВ рдорд┐рд▓реЗред
Passport.js рд╕реАрдзреЗ рдкрд╣реБрдВрдЪ рдирд┐рдпрдВрддреНрд░рдг рдХреА рдирд┐рдЧрд░рд╛рдиреА рдХрд░реЗрдЧрд╛ред OAuth2 рд╕рд░реНрд╡рд░ рдХреЗ рд▓рд┐рдП, рдПрдХ рд╣реА рд▓реЗрдЦрдХ, oauth2orize рд╕реЗ рдПрдХ рд╕рдорд╛рдзрд╛рди рдЙрдкрдпреЛрдЧреА рд╣реИ ред рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛, рдЯреЛрдХрди MongoDB рдореЗрдВ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд┐рдП рдЬрд╛рдПрдВрдЧреЗред
рдкрд╣рд▓реЗ рд╣рдореЗрдВ рдЙрди рд╕рднреА рдореЙрдбреНрдпреВрд▓ рдХреЛ рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдирд╛ рд╣реЛрдЧрд╛ рдЬрд┐рдирдХреА рд╣рдореЗрдВ рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ:

рдлрд┐рд░, mongoose.js рдореЗрдВ рдЖрдкрдХреЛ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдФрд░ рдЯреЛрдХрди рдХреЗ рд▓рд┐рдП рдпреЛрдЬрдирд╛рдПрдБ рдЬреЛрдбрд╝рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ:

 var crypto = require('crypto'); // User var User = new Schema({ username: { type: String, unique: true, required: true }, hashedPassword: { type: String, required: true }, salt: { type: String, required: true }, created: { type: Date, default: Date.now } }); User.methods.encryptPassword = function(password) { return crypto.createHmac('sha1', this.salt).update(password).digest('hex'); //more secure - return crypto.pbkdf2Sync(password, this.salt, 10000, 512); }; User.virtual('userId') .get(function () { return this.id; }); User.virtual('password') .set(function(password) { this._plainPassword = password; this.salt = crypto.randomBytes(32).toString('base64'); //more secure - this.salt = crypto.randomBytes(128).toString('base64'); this.hashedPassword = this.encryptPassword(password); }) .get(function() { return this._plainPassword; }); User.methods.checkPassword = function(password) { return this.encryptPassword(password) === this.hashedPassword; }; var UserModel = mongoose.model('User', User); // Client var Client = new Schema({ name: { type: String, unique: true, required: true }, clientId: { type: String, unique: true, required: true }, clientSecret: { type: String, required: true } }); var ClientModel = mongoose.model('Client', Client); // AccessToken var AccessToken = new Schema({ userId: { type: String, required: true }, clientId: { type: String, required: true }, token: { type: String, unique: true, required: true }, created: { type: Date, default: Date.now } }); var AccessTokenModel = mongoose.model('AccessToken', AccessToken); // RefreshToken var RefreshToken = new Schema({ userId: { type: String, required: true }, clientId: { type: String, required: true }, token: { type: String, unique: true, required: true }, created: { type: Date, default: Date.now } }); var RefreshTokenModel = mongoose.model('RefreshToken', RefreshToken); module.exports.UserModel = UserModel; module.exports.ClientModel = ClientModel; module.exports.AccessTokenModel = AccessTokenModel; module.exports.RefreshTokenModel = RefreshTokenModel; 


рд╡рд░реНрдЪреБрдЕрд▓ рдкрд╛рд╕рд╡рд░реНрдб рдкреНрд░реЙрдкрд░реНрдЯреА рдЗрд╕ рдмрд╛рдд рдХрд╛ рдПрдХ рдЙрджрд╛рд╣рд░рдг рд╣реИ рдХрд┐ рдХрд┐рд╕реА рдореЙрдбрд▓ рдореЗрдВ рдореИрдВрдЧреЛрдЬрд╝ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рд▓реЙрдЬрд┐рдХ рдХреЛ рдХреИрд╕реЗ рдПрдореНрдмреЗрдб рдХрд░ рд╕рдХрддрд╛ рд╣реИред рд╣реИрд╢ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ, рдПрд▓реНрдЧреЛрд░рд┐рджрдо рдФрд░ рдирдордХ - рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ рдирд╣реАрдВ, рд╣рдо рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЗ рд╡рд┐рд╡рд░рдг рдореЗрдВ рдирд╣реАрдВ рдЬрд╛рдПрдВрдЧреЗред

рддреЛ, рдбреЗрдЯрд╛рдмреЗрд╕ рдореЗрдВ рдСрдмреНрдЬреЗрдХреНрдЯреНрд╕:
  1. рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ - рдПрдХ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЬрд┐рд╕рдХрд╛ рдирд╛рдо, рдкрд╛рд╕рд╡рд░реНрдб рд╣реИрд╢ рдФрд░ рдЙрд╕рдХреЗ рдкрд╛рд╕рд╡рд░реНрдб рдХрд╛ рдирдордХ рд╣реИред
  2. рдХреНрд▓рд╛рдЗрдВрдЯ - рдПрдХ рдХреНрд▓рд╛рдЗрдВрдЯ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдЬрд┐рд╕реЗ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреА рдУрд░ рд╕реЗ рдПрдХреНрд╕реЗрд╕ рджреА рдЬрд╛рддреА рд╣реИред рдЙрдирдХрд╛ рдПрдХ рдирд╛рдо рдФрд░ рдПрдХ рдЧреБрдкреНрдд рдХреЛрдб рд╣реИред
  3. AccessToken - рдХреНрд▓рд╛рдЗрдВрдЯ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЛ рдЬрд╛рд░реА рдХрд┐рдпрд╛ рдЧрдпрд╛ рдПрдХ рдЯреЛрдХрди (рд╡рд╛рд╣рдХ рдкреНрд░рдХрд╛рд░) рд╕рдордп рдореЗрдВ рд╕реАрдорд┐рдд рд╣реИред
  4. RefreshToken - рдПрдХ рдЕрдиреНрдп рдкреНрд░рдХрд╛рд░ рдХрд╛ рдЯреЛрдХрди, рдЖрдкрдХреЛ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рд╕реЗ рдкрд╛рд╕рд╡рд░реНрдб рдкреБрди: рдЕрдиреБрд░реЛрдз рдХрд┐рдП рдмрд┐рдирд╛ рдПрдХ рдирдП рд╡рд╛рд╣рдХ рдЯреЛрдХрди рдХрд╛ рдЕрдиреБрд░реЛрдз рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред


Config.json рдореЗрдВ, рдЯреЛрдХрди рдЬреАрд╡рдирдХрд╛рд▓ рдЬреЛрдбрд╝реЗрдВ:
 { "port" : 1337, "security": { "tokenLife" : 3600 }, "mongoose": { "uri": "mongodb://localhost/testAPI" } } 


рд╣рдо OAuth2 рд╕рд░реНрд╡рд░ рдФрд░ рдкреНрд░рд╛рдзрд┐рдХрд░рдг рддрд░реНрдХ рдХреЛ рдЕрд▓рдЧ-рдЕрд▓рдЧ рдореЙрдбреНрдпреВрд▓ рдореЗрдВ рдЕрд▓рдЧ рдХрд░рддреЗ рд╣реИрдВред Oauth.js рдиреЗ рдкрд╛рд╕рдкреЛрд░реНрдЯ.js рдХреА "рд░рдгрдиреАрддрд┐рдпреЛрдВ" рдХрд╛ рд╡рд░реНрдгрди рдХрд┐рдпрд╛ рд╣реИ, рд╣рдо рдЙрдирдореЗрдВ рд╕реЗ 3 рдХреЛ рдХрдиреЗрдХреНрдЯ рдХрд░рддреЗ рд╣реИрдВ - 2 рдХреЛ OAuth2 рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдирд╛рдо-рдкрд╛рд╕рд╡рд░реНрдб рдкреНрд░рд╡рд╛рд╣, 1 рдЯреЛрдХрди рдХреА рдЬрд╛рдВрдЪ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдПред

 var config = require('./config'); var passport = require('passport'); var BasicStrategy = require('passport-http').BasicStrategy; var ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy; var BearerStrategy = require('passport-http-bearer').Strategy; var UserModel = require('./mongoose').UserModel; var ClientModel = require('./mongoose').ClientModel; var AccessTokenModel = require('./mongoose').AccessTokenModel; var RefreshTokenModel = require('./mongoose').RefreshTokenModel; passport.use(new BasicStrategy( function(username, password, done) { ClientModel.findOne({ clientId: username }, function(err, client) { if (err) { return done(err); } if (!client) { return done(null, false); } if (client.clientSecret != password) { return done(null, false); } return done(null, client); }); } )); passport.use(new ClientPasswordStrategy( function(clientId, clientSecret, done) { ClientModel.findOne({ clientId: clientId }, function(err, client) { if (err) { return done(err); } if (!client) { return done(null, false); } if (client.clientSecret != clientSecret) { return done(null, false); } return done(null, client); }); } )); passport.use(new BearerStrategy( function(accessToken, done) { AccessTokenModel.findOne({ token: accessToken }, function(err, token) { if (err) { return done(err); } if (!token) { return done(null, false); } if( Math.round((Date.now()-token.created)/1000) > config.get('security:tokenLife') ) { AccessTokenModel.remove({ token: accessToken }, function (err) { if (err) return done(err); }); return done(null, false, { message: 'Token expired' }); } UserModel.findById(token.userId, function(err, user) { if (err) { return done(err); } if (!user) { return done(null, false, { message: 'Unknown user' }); } var info = { scope: '*' } done(null, user, info); }); }); } )); 


рдЯреЛрдХрди рдЬрд╛рд░реА рдХрд░рдиреЗ рдФрд░ рдЕрджреНрдпрддрди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП Oauth2.js рдЬрд┐рдореНрдореЗрджрд╛рд░ рд╣реИред рдПрдХ рдПрдХреНрд╕рдЪреЗрдВрдЬ рд░рдгрдиреАрддрд┐ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдирд╛рдо-рдкрд╛рд╕рд╡рд░реНрдб рдкреНрд░рд╡рд╛рд╣ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдПрдХ рдЯреЛрдХрди рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╣реИ, рдПрдХ рдЕрдиреНрдп рддрд╛рдЬрд╝рд╛_рдЯреЛрдХрди рдХрд╛ рдЖрджрд╛рди-рдкреНрд░рджрд╛рди рдХрд░рдирд╛ рд╣реИред

 var oauth2orize = require('oauth2orize'); var passport = require('passport'); var crypto = require('crypto'); var config = require('./config'); var UserModel = require('./mongoose').UserModel; var ClientModel = require('./mongoose').ClientModel; var AccessTokenModel = require('./mongoose').AccessTokenModel; var RefreshTokenModel = require('./mongoose').RefreshTokenModel; // create OAuth 2.0 server var server = oauth2orize.createServer(); // Exchange username & password for access token. server.exchange(oauth2orize.exchange.password(function(client, username, password, scope, done) { UserModel.findOne({ username: username }, function(err, user) { if (err) { return done(err); } if (!user) { return done(null, false); } if (!user.checkPassword(password)) { return done(null, false); } RefreshTokenModel.remove({ userId: user.userId, clientId: client.clientId }, function (err) { if (err) return done(err); }); AccessTokenModel.remove({ userId: user.userId, clientId: client.clientId }, function (err) { if (err) return done(err); }); var tokenValue = crypto.randomBytes(32).toString('base64'); var refreshTokenValue = crypto.randomBytes(32).toString('base64'); var token = new AccessTokenModel({ token: tokenValue, clientId: client.clientId, userId: user.userId }); var refreshToken = new RefreshTokenModel({ token: refreshTokenValue, clientId: client.clientId, userId: user.userId }); refreshToken.save(function (err) { if (err) { return done(err); } }); var info = { scope: '*' } token.save(function (err, token) { if (err) { return done(err); } done(null, tokenValue, refreshTokenValue, { 'expires_in': config.get('security:tokenLife') }); }); }); })); // Exchange refreshToken for access token. server.exchange(oauth2orize.exchange.refreshToken(function(client, refreshToken, scope, done) { RefreshTokenModel.findOne({ token: refreshToken }, function(err, token) { if (err) { return done(err); } if (!token) { return done(null, false); } if (!token) { return done(null, false); } UserModel.findById(token.userId, function(err, user) { if (err) { return done(err); } if (!user) { return done(null, false); } RefreshTokenModel.remove({ userId: user.userId, clientId: client.clientId }, function (err) { if (err) return done(err); }); AccessTokenModel.remove({ userId: user.userId, clientId: client.clientId }, function (err) { if (err) return done(err); }); var tokenValue = crypto.randomBytes(32).toString('base64'); var refreshTokenValue = crypto.randomBytes(32).toString('base64'); var token = new AccessTokenModel({ token: tokenValue, clientId: client.clientId, userId: user.userId }); var refreshToken = new RefreshTokenModel({ token: refreshTokenValue, clientId: client.clientId, userId: user.userId }); refreshToken.save(function (err) { if (err) { return done(err); } }); var info = { scope: '*' } token.save(function (err, token) { if (err) { return done(err); } done(null, tokenValue, refreshTokenValue, { 'expires_in': config.get('security:tokenLife') }); }); }); }); })); // token endpoint exports.token = [ passport.authenticate(['basic', 'oauth2-client-password'], { session: false }), server.token(), server.errorHandler() ] 


рдЗрди рдореЙрдбреНрдпреВрд▓ рдХреЛ рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП, server.js рдореЗрдВ рдЬреЛрдбрд╝реЗрдВ:

 var oauth2 = require('./libs/oauth2'); app.use(passport.initialize()); require('./libs/auth'); app.post('/oauth/token', oauth2.token); app.get('/api/userInfo', passport.authenticate('bearer', { session: false }), function(req, res) { // req.authInfo is set using the `info` argument supplied by // `BearerStrategy`. It is typically used to indicate scope of the token, // and used in access control checks. For illustrative purposes, this // example simply returns the scope in the response. res.json({ user_id: req.user.userId, name: req.user.username, scope: req.authInfo.scope }) } ); 


рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рд╕реБрд░рдХреНрд╖рд╛ рд╕реНрдерд╛рдиреАрдп рд╣реЛрд╕реНрдЯ рдкрддреЗ рдкрд░ рд╣реИ: 1337 / рдПрдкреАрдЖрдИ / userInfoред

рдкреНрд░рд╛рдзрд┐рдХрд░рдг рддрдВрддреНрд░ рдХреЗ рд╕рдВрдЪрд╛рд▓рди рдХреА рдЬрд╛рдВрдЪ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдкрдХреЛ рдбреЗрдЯрд╛рдмреЗрд╕ рдореЗрдВ рдПрдХ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдФрд░ рдЧреНрд░рд╛рд╣рдХ рдмрдирд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред рдореИрдВ Node.js рдкрд░ рдПрдХ рдЖрд╡реЗрджрди рджреВрдВрдЧрд╛, рдЬреЛ рдЖрд╡рд╢реНрдпрдХ рд╡рд╕реНрддреБрдУрдВ рдХрд╛ рдирд┐рд░реНрдорд╛рдг рдХрд░реЗрдЧрд╛ рдФрд░ рд╕рдВрдЧреНрд░рд╣ рд╕реЗ рдЕрдирд╛рд╡рд╢реНрдпрдХ рдХреЛ рд╣рдЯрд╛ рджреЗрдЧрд╛ред рдпрд╣ рдкрд░реАрдХреНрд╖рдг рдХреЗ рджреМрд░рд╛рди рдЯреЛрдХрди рдФрд░ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдХреЗ рдбреЗрдЯрд╛рдмреЗрд╕ рдХреЛ рдЬрд▓реНрджреА рд╕реЗ рд╕рд╛рдл рдХрд░рдиреЗ рдореЗрдВ рдорджрдж рдХрд░рддрд╛ рд╣реИ, рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдПрдХ рд▓реЙрдиреНрдЪ рдкрд░реНрдпрд╛рдкреНрдд рд╣реЛрдЧрд╛ :)

 var log = require('./libs/log')(module); var mongoose = require('./libs/mongoose').mongoose; var UserModel = require('./libs/mongoose').UserModel; var ClientModel = require('./libs/mongoose').ClientModel; var AccessTokenModel = require('./libs/mongoose').AccessTokenModel; var RefreshTokenModel = require('./libs/mongoose').RefreshTokenModel; var faker = require('Faker'); UserModel.remove({}, function(err) { var user = new UserModel({ username: "andrey", password: "simplepassword" }); user.save(function(err, user) { if(err) return log.error(err); else log.info("New user - %s:%s",user.username,user.password); }); for(i=0; i<4; i++) { var user = new UserModel({ username: faker.random.first_name().toLowerCase(), password: faker.Lorem.words(1)[0] }); user.save(function(err, user) { if(err) return log.error(err); else log.info("New user - %s:%s",user.username,user.password); }); } }); ClientModel.remove({}, function(err) { var client = new ClientModel({ name: "OurService iOS client v1", clientId: "mobileV1", clientSecret:"abc123456" }); client.save(function(err, client) { if(err) return log.error(err); else log.info("New client - %s:%s",client.clientId,client.clientSecret); }); }); AccessTokenModel.remove({}, function (err) { if (err) return log.error(err); }); RefreshTokenModel.remove({}, function (err) { if (err) return log.error(err); }); setTimeout(function() { mongoose.disconnect(); }, 3000); 


рдпрджрд┐ рдЖрдкрдиреЗ рдПрдХ рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдХреЗ рд╕рд╛рде рдбреЗрдЯрд╛ рдмрдирд╛рдпрд╛ рд╣реИ, рддреЛ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдкреНрд░рд╛рдзрд┐рдХрд░рдг рдЖрджреЗрд╢ рдЖрдкрдХреЗ рдЕрдиреБрд░реВрдк рд╣реЛрдВрдЧреЗред рдореИрдВ рдЖрдкрдХреЛ рдпрд╛рдж рджрд┐рд▓рд╛ рджреВрдВ рдХрд┐ рдореИрдВ httpie рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣рд╛ рд╣реВрдВ ред

 http POST http://localhost:1337/oauth/token grant_type=password client_id=mobileV1 client_secret=abc123456 username=andrey password=simplepassword http POST http://localhost:1337/oauth/token grant_type=refresh_token client_id=mobileV1 client_secret=abc123456 refresh_token=TOKEN http http://localhost:1337/api/userinfo Authorization:'Bearer TOKEN' 


рдЪреЗрддрд╛рд╡рдиреА! рдЙрддреНрдкрд╛рджрди рд╕рд░реНрд╡рд░ рдкрд░ HTTPS рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░реЗрдВ, рдпрд╣ OAuth 2 рд╡рд┐рдирд┐рд░реНрджреЗрд╢рди рджреНрд╡рд╛рд░рд╛ рдирд┐рд╣рд┐рдд рд╣реИред рдФрд░ рд╕рд╣реА рдкрд╛рд╕рд╡рд░реНрдб рд╣реИрд╢рд┐рдВрдЧ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдордд рднреВрд▓рдирд╛ред рдЗрд╕ рдЙрджрд╛рд╣рд░рдг рдХреЗ рд╕рд╛рде https рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдирд╛ рдореБрд╢реНрдХрд┐рд▓ рдирд╣реАрдВ рд╣реИ, рдиреЗрдЯрд╡рд░реНрдХ рдкрд░ рдХрдИ рдЙрджрд╛рд╣рд░рдг рд╣реИрдВред
рдЖрдкрдХреЛ рдпрд╛рдж рджрд┐рд▓рд╛ рджреВрдВ рдХрд┐ GitHub рдХреЗ рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА рдореЗрдВ рд╕рднреА рдХреЛрдб рдирд┐рд╣рд┐рдд рд╣реИрдВред
рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдкрдХреЛ рдирд┐рд░реНрджреЗрд╢рд┐рдХрд╛ рдореЗрдВ mongod npm install рдЪрд▓рд╛рдиреЗ рдХреА рдЬрд░реВрд░рдд рд╣реИ, mongod , node dataGen.js (рдирд┐рд╖реНрдкрд╛рджрди рдХреЗ рд▓рд┐рдП рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░реЗрдВ), рдФрд░ рдлрд┐рд░ node server.js ред

рдпрджрд┐ рд▓реЗрдЦ рдХреЗ рдХрд┐рд╕реА рднреА рднрд╛рдЧ рдХреЛ рдЕрдзрд┐рдХ рд╡рд┐рд╡рд░рдг рдореЗрдВ рд╡рд░реНрдгрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП, рддреЛ рдХреГрдкрдпрд╛ рдЯрд┐рдкреНрдкрдгрд┐рдпреЛрдВ рдореЗрдВ рдЗрд╕реЗ рдЗрдВрдЧрд┐рдд рдХрд░реЗрдВред рд╕рд╛рдордЧреНрд░реА рдХреЛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ рдФрд░ рд╕рдореАрдХреНрд╖рд╛рдПрдБ рдЙрдкрд▓рдмреНрдз рд╣реЛрдиреЗ рдХреЗ рд░реВрдк рдореЗрдВ рдЕрдкрдбреЗрдЯ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред

рд╕рдВрдХреНрд╖реЗрдк рдореЗрдВ, рдореИрдВ рдХрд╣рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ рдХрд┐ рдиреЛрдб.рдЬреЗрдПрд╕ рдПрдХ рдЕрдЪреНрдЫрд╛, рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рд╕рд░реНрд╡рд░ рд╕рдорд╛рдзрд╛рди рд╣реИред рджрд╕реНрддрд╛рд╡реЗрдЬрд╝-рдЙрдиреНрдореБрдЦ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдХреЗ рд╕рд╛рде MongoDB рдПрдХ рдмрд╣реБрдд рд╣реА рдЕрд╕рд╛рдорд╛рдиреНрдп, рд▓реЗрдХрд┐рди рдирд┐рд╕реНрд╕рдВрджреЗрд╣ рдЙрдкрдпреЛрдЧреА рдЙрдкрдХрд░рдг рд╣реИ, рдЬрд┐рдирдореЗрдВ рд╕реЗ рдЕрдзрд┐рдХрд╛рдВрд╢ рд╡рд┐рд╢реЗрд╖рддрд╛рдУрдВ рдХрд╛ рдореИрдВрдиреЗ рдЕрднреА рддрдХ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд┐рдпрд╛ рд╣реИред Node.js рдХреЗ рдЖрд╕рдкрд╛рд╕ рдПрдХ рдмрд╣реБрдд рдмрдбрд╝рд╛ рд╕рдореБрджрд╛рдп рд╣реИ, рдЬрд╣рд╛рдВ рдХрдИ рдУрдкрди-рд╕реЛрд░реНрд╕ рд╡рд┐рдХрд╛рд╕ рд╣реИрдВред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, oauth2orize рдФрд░ рдкрд╛рд╕рдкреЛрд░реНрдЯ рдХреЗ рдирд┐рд░реНрдорд╛рддрд╛, рдЬреЗрд░реЗрдб рд╣реИрдиреНрд╕рди рдиреЗ рдЕрджреНрднреБрдд рдкрд░рд┐рдпреЛрдЬрдирд╛рдПрдВ рдХреА рд╣реИрдВ рдЬреЛ рдХрд┐ рдареАрдХ рд╕реЗ рд╕рдВрд░рдХреНрд╖рд┐рдд рдкреНрд░рдгрд╛рд▓рд┐рдпреЛрдВ рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЛ рдпрдерд╛рд╕рдВрднрд╡ рдЖрд╕рд╛рди рдмрдирд╛рддреЗ рд╣реИрдВред

Source: https://habr.com/ru/post/In193458/


All Articles