首页 > 代码库 > NodeJS学习笔记(一)——搭建开发框架Express,实现Web网站登录验证

NodeJS学习笔记(一)——搭建开发框架Express,实现Web网站登录验证

  JS是脚本语言,脚本语言都需要一个解析器才能运行。对于写在HTML页面里的JS,浏览器充当了解析器的角色。而对于需要独立运行的JS,NodeJS就是一个解析器。每一种解析器都是一个运行环境,不但允许JS定义各种数据结构,进行各种计算,还允许JS使用运行环境提供的内置对象和方法做一些事情。例如运行在浏览器中的JS的用途是操作DOM,浏览器就提供了document之类的内置对象。而运行在NodeJS中的JS的用途是操作磁盘文件或搭建HTTP服务器,NodeJS就相应提供了fs、http等内置对象。Express作为NodeJS的Web应用框架,可以帮助我们快速开发Web网站。


  开发环境

  • NodeJS:v0.10.30
  • npm:1.4.21
  • OS:Win7旗舰版 32bit
  • Express:4.2.0
  • MongoDB:2.6.3
E:\project> node -vv0.10.30E:\project> npm -v1.4.21E:\project> express -V4.2.0

  1、建立工程

  使用express命令建立工程,并支持ejs:

E:\project> express -e nodejs-demo   create : nodejs-demo   create : nodejs-demo/package.json   create : nodejs-demo/app.js   create : nodejs-demo/public   create : nodejs-demo/public/javascripts   create : nodejs-demo/public/images   create : nodejs-demo/public/stylesheets   create : nodejs-demo/public/stylesheets/style.css   create : nodejs-demo/routes   create : nodejs-demo/routes/index.js   create : nodejs-demo/routes/users.js   create : nodejs-demo/views   create : nodejs-demo/views/index.ejs   create : nodejs-demo/views/error.ejs   create : nodejs-demo/bin   create : nodejs-demo/bin/www   install dependencies:     $ cd nodejs-demo && npm install   run the app:     $ DEBUG=nodejs-demo ./bin/wwwE:\project>

  根据提示下载依赖包:

E:\project> cd .\nodejs-demoE:\project\nodejs-demo> npm installnpm WARN deprecated static-favicon@1.0.2: use serve-favicon modulestatic-favicon@1.0.2 node_modules\static-favicondebug@0.7.4 node_modules\debugejs@0.8.8 node_modules\ejscookie-parser@1.0.1 node_modules\cookie-parser├── cookie-signature@1.0.3└── cookie@0.1.0morgan@1.0.1 node_modules\morgan└── bytes@0.3.0body-parser@1.0.2 node_modules\body-parser├── qs@0.6.6├── raw-body@1.1.7 (bytes@1.0.0, string_decoder@0.10.25-1)└── type-is@1.1.0 (mime@1.2.11)express@4.2.0 node_modules\express├── parseurl@1.0.1├── utils-merge@1.0.0├── cookie@0.1.2├── merge-descriptors@0.0.2├── escape-html@1.0.1├── range-parser@1.0.0├── fresh@0.2.2├── cookie-signature@1.0.3├── debug@0.8.1├── methods@1.0.0├── buffer-crc32@0.2.1├── serve-static@1.1.0├── path-to-regexp@0.1.2├── qs@0.6.6├── send@0.3.0 (debug@0.8.0, mime@1.2.11)├── accepts@1.0.1 (negotiator@0.4.7, mime@1.2.11)└── type-is@1.1.0 (mime@1.2.11)E:\project\nodejs-demo>

  工程建立成功,启动服务:

E:\project\nodejs-demo> npm start> nodejs-demo@0.0.1 start E:\project\nodejs-demo> node ./bin/www

  本地3000端口被打开,在浏览器地址栏输入localhost:3000,访问成功。


  2、目录结构

  • bin——存放命令行程序。
  • node_modules——存放所有的项目依赖库。
  • public——存放静态文件,包括css、js、img等。
  • routes——存放路由文件。
  • views——存放页面文件(ejs模板)。
  • app.js——程序启动文件。
  • package.json——项目依赖配置及开发者信息。
E:\project\nodejs-demo> dir    目录: E:\project\nodejs-demoMode                LastWriteTime     Length Name----                -------------     ------ ----d----         2014/8/16     21:55            bind----         2014/8/16     22:03            node_modulesd----         2014/8/16     21:55            publicd----         2014/8/16     21:55            routesd----         2014/8/16     21:55            views-a---         2014/8/16     21:55       1375 app.js-a---         2014/8/16     21:55        327 package.json

  3、Express配置文件

  打开app.js:

 1 var express = require(‘express‘); 2 var path = require(‘path‘); 3 var favicon = require(‘static-favicon‘); 4 var logger = require(‘morgan‘); 5 var cookieParser = require(‘cookie-parser‘); 6 var bodyParser = require(‘body-parser‘); 7  8 var routes = require(‘./routes/index‘); 9 var users = require(‘./routes/users‘);10 11 var app = express();12 13 // view engine setup14 app.set(‘views‘, path.join(__dirname, ‘views‘));15 app.set(‘view engine‘, ‘ejs‘);16 17 app.use(favicon());18 app.use(logger(‘dev‘));19 app.use(bodyParser.json());20 app.use(bodyParser.urlencoded());21 app.use(cookieParser());22 app.use(express.static(path.join(__dirname, ‘public‘)));23 24 app.use(‘/‘, routes);25 app.use(‘/users‘, users);26 27 /// catch 404 and forward to error handler28 app.use(function(req, res, next) {29     var err = new Error(‘Not Found‘);30     err.status = 404;31     next(err);32 });33 34 /// error handlers35 36 // development error handler37 // will print stacktrace38 if (app.get(‘env‘) === ‘development‘) {39     app.use(function(err, req, res, next) {40         res.status(err.status || 500);41         res.render(‘error‘, {42             message: err.message,43             error: err44         });45     });46 }47 48 // production error handler49 // no stacktraces leaked to user50 app.use(function(err, req, res, next) {51     res.status(err.status || 500);52     res.render(‘error‘, {53         message: err.message,54         error: {}55     });56 });57 58 59 module.exports = app;

  4、Ejs模板

  修改app.js,让ejs模板文件使用扩展名为html的文件:

13 // view engine setup14 app.set(‘views‘, path.join(__dirname, ‘views‘));15 //app.set(‘view engine‘, ‘ejs‘);16 app.engine(‘html‘, require(‘ejs‘).renderFile);17 app.set(‘view engine‘, ‘html‘);

  修改完成后,重命名views/index.ejs为views/index.html。重启服务,访问成功。


  5、安装常用库及页面分离

  添加bootstrap和jQuery:

E:\project\nodejs-demo> npm install bootstrapbootstrap@3.2.0 node_modules\bootstrapE:\project\nodejs-demo> npm install jqueryjquery@2.1.1 node_modules\jqueryE:\project\nodejs-demo>

  接下来,把index.html分成三个部分:

  • header.html——页面头部区域。
  • index.html——页面内容区域。
  • footer.html——页面底部区域。

  header.html

1 <!DOCTYPE html>2 <html lang="en">3 <head>4     <meta charset="utf-8">5     <title><%= title %></title>6     <!-- Bootstrap -->7     <link href="/stylesheets/bootstrap.min.css" rel="stylesheet" media="screen">8 </head>9 <body screen_capture_injected="true">

  index.html

1 <% include header.html %>2 <h1><%= title %></h1>3 <p>Welcome to <%= title %></p>4 <% include footer.html %>

  footer.html

1     <script src="/javascripts/jquery.min.js"></script>2     <script src="/javascripts/bootstrap.min.js"></script>3 </body>4 </html>

  重启服务,访问成功。


  6、路由

  登录设计:

访问路径页面描述
/index.html不需要登录,可以直接访问。
/homehome.html必须用户登录以后,才可以访问。
/loginlogin.html登录页面,用户名密码输入正确,自动跳转到home.html。
/logout退出登录后,自动跳转到index.html。

  打开app.js文件,增加路由配置:

26 app.use(‘/‘, routes);27 app.use(‘/users‘, users);28 app.use(‘/login‘, routes);29 app.use(‘/logout‘, routes);30 app.use(‘/home‘, routes);

  打开routes/index.js文件,添加对应方法:

 1 var express = require(‘express‘); 2 var router = express.Router(); 3  4 /* GET home page. */ 5 router.get(‘/‘, function(req, res) { 6   res.render(‘index‘, { title: ‘Express‘ }); 7 }); 8  9 router.route(‘/login‘)10 .get(function(req, res) {11     res.render(‘login‘, { title: ‘用户登录‘ });12 })13 .post(function(req, res) {14     var user={15         username: ‘admin‘,16         password: ‘123456‘17     }18     if(req.body.username === user.username && req.body.password === user.password){19         res.redirect(‘/home‘);20     }21     res.redirect(‘/login‘);22 });23 24 router.get(‘/logout‘, function(req, res) {25     res.redirect(‘/‘);26 });27 28 router.get(‘/home‘, function(req, res) {29     var user={30         username:‘admin‘,31         password:‘123456‘32     }33     res.render(‘home‘, { title: ‘Home‘, user: user });34 });35 36 module.exports = router;

  创建views/login.html和views/home.html两个文件:

  login.html

 1 <% include header.html %> 2 <div class="container"> 3     <form class="col-sm-offset-4 col-sm-4 form-horizontal" role="form" method="post"> 4         <fieldset> 5             <legend>用户登录</legend> 6             <div class="form-group"> 7                 <label class="col-sm-3 control-label" for="username">用户名</label> 8                 <div class="col-sm-9"> 9                     <input type="text" class="form-control" id="username" name="username" placeholder="用户名" required>10                 </div>11             </div>12             <div class="form-group">13                 <label class="col-sm-3 control-label" for="password">密码</label>14                 <div class="col-sm-9">15                     <input type="password" class="form-control" id="password" name="password" placeholder="密码" required>16                 </div>17             </div>18             <div class="form-group">19                 <div class="col-sm-offset-3 col-sm-9">20                     <button type="submit" class="btn btn-primary">登录</button>21                 </div>22             </div>23         </fieldset>24     </form>25 </div>26 <% include footer.html %>

  home.html

1 <% include header.html %>2 <h1>Welcome <%= user.username %>, 欢迎登录!!</h1>3 <a class="btn" href="/logout">退出</a>4 <% include footer.html %>

  修改index.html,增加登录链接:

1 <% include header.html %>2     <h1>Welcome to <%= title %></h1>3     <p><a href="/login">登录</a></p>4 <% include footer.html %>

  路由及页面已准备好,重启服务,访问成功。


  7、session

  安装中间件express-session:

E:\project\nodejs-demo> npm install express-sessionexpress-session@1.7.5 node_modules\express-session├── cookie@0.1.2├── cookie-signature@1.0.4├── on-headers@1.0.0├── utils-merge@1.0.0├── parseurl@1.3.0├── buffer-crc32@0.2.3├── depd@0.4.4├── debug@1.0.4 (ms@0.6.2)└── uid-safe@1.0.1 (base64-url@1.0.0, mz@1.0.0)E:\project\nodejs-demo>

  安装中间件connect-mongodb:

E:\project\nodejs-demo> npm install connect-mongodb> kerberos@0.0.3 install E:\project\nodejs-demo\node_modules\connect-mongodb\node_modules\mongodb\node_modules\kerberos> (node-gyp rebuild 2> builderror.log) || (exit 0)|E:\project\nodejs-demo\node_modules\connect-mongodb\node_modules\mongodb\node_modules\kerberos>node "C:\Program Files\nodejs\node_modules\npm\bin\node-gyp-bin\..\..\node_modules\node-gyp\bin\node-gyp.js" rebuild|> bson@0.2.11 install E:\project\nodejs-demo\node_modules\connect-mongodb\node_modules\mongodb\node_modules\bson> (node-gyp rebuild 2> builderror.log) || (exit 0)E:\project\nodejs-demo\node_modules\connect-mongodb\node_modules\mongodb\node_modules\bson>node "C:\Program Files\nodejs\node_modules\npm\bin\node-gyp-bin\\..\..\node_modules\node-gyp\bin\node-gyp.js" rebuildconnect-mongodb@1.1.5 node_modules\connect-mongodb├── connect@1.9.2 (mime@1.2.11, formidable@1.0.15, qs@1.2.2)└── mongodb@1.4.8 (kerberos@0.0.3, readable-stream@1.0.27-1, bson@0.2.11)E:\project\nodejs-demo>

  安装中间件mongodb:

E:\project\nodejs-demo> npm install mongodb-> kerberos@0.0.3 install E:\project\nodejs-demo\node_modules\mongodb\node_modules\kerberos> (node-gyp rebuild 2> builderror.log) || (exit 0)-E:\project\nodejs-demo\node_modules\mongodb\node_modules\kerberos>node "C:\Program Files\nodejs\node_modules\npm\bin\node-gyp-bin\\..\..\node_modules\node-gyp\bin\node-gyp.js" rebuild|> bson@0.2.11 install E:\project\nodejs-demo\node_modules\mongodb\node_modules\bson> (node-gyp rebuild 2> builderror.log) || (exit 0)E:\project\nodejs-demo\node_modules\mongodb\node_modules\bson>node "C:\Program Files\nodejs\node_modules\npm\bin\node-gyp-bin\\..\..\node_modules\node-gyp\bin\node-gyp.js" rebuildmongodb@1.4.8 node_modules\mongodb├── kerberos@0.0.3├── readable-stream@1.0.27-1 (isarray@0.0.1, string_decoder@0.10.25-1, inherits@2.0.1, core-util-is@1.0.1)└── bson@0.2.11 (nan@1.2.0)E:\project\nodejs-demo>

  添加database/settings.js和database/msession.js这两个文件:

  settings.js

1 module.exports = {2     COOKIE_SECRET: ‘ywang1724.com‘, 3     URL: ‘mongodb://127.0.0.1:27017/nodedb‘,4     DB: ‘nodedb‘, 5     HOST: ‘127.0.0.1‘,6     PORT: 27017,7     USERNAME: ‘admin‘,8     PASSWORD: ‘123456‘9 };

  msession.js

1 var Settings = require(‘./settings‘);2 var Db = require(‘mongodb‘).Db;3 var Server = require(‘mongodb‘).Server; 4 var db = new Db(Settings.DB, new Server(Settings.HOST, Settings.PORT, {auto_reconnect:true, native_parser: true}),{safe: false});5 6 module.exports = db;

  修改app.js文件:

 1 var express = require(‘express‘); 2 var path = require(‘path‘); 3 var favicon = require(‘static-favicon‘); 4 var logger = require(‘morgan‘); 5 var cookieParser = require(‘cookie-parser‘); 6 var bodyParser = require(‘body-parser‘); 7  8 //采用connect-mongodb中间件作为Session存储   9 var session = require(‘express-session‘);  10 var Settings = require(‘./database/settings‘);  11 var MongoStore = require(‘connect-mongodb‘);  12 var db = require(‘./database/msession‘); 13 14 var routes = require(‘./routes/index‘);15 var users = require(‘./routes/users‘);16 17 var app = express();18 19 // view engine setup20 app.set(‘views‘, path.join(__dirname, ‘views‘));21 //app.set(‘view engine‘, ‘ejs‘);22 app.engine(‘html‘, require(‘ejs‘).renderFile);23 app.set(‘view engine‘, ‘html‘);24 25 app.use(favicon());26 app.use(logger(‘dev‘));27 app.use(bodyParser.json());28 app.use(bodyParser.urlencoded());29 app.use(cookieParser());30 //session配置31 app.use(session({32     cookie: { maxAge: 600000 },33     secret: Settings.COOKIE_SECRET,34     store: new MongoStore({  35         username: Settings.USERNAME,36         password: Settings.PASSWORD,37         url: Settings.URL,38         db: db})39 }))40 app.use(function(req, res, next){41     res.locals.user = req.session.user;42     next();43 });44 45 app.use(express.static(path.join(__dirname, ‘public‘)));46 47 ......

  修改index.js文件:

 1 var express = require(‘express‘); 2 var router = express.Router(); 3  4 /* GET home page. */ 5 router.get(‘/‘, function(req, res) { 6     res.render(‘index‘, { title: ‘Express‘ }); 7 }); 8  9 router.route(‘/login‘)10 .get(function(req, res) {11     res.render(‘login‘, { title: ‘用户登录‘ });12 })13 .post(function(req, res) {14     var user = {15         username: ‘admin‘,16         password: ‘123456‘17     }18     if(req.body.username === user.username && req.body.password === user.password){19         req.session.user = user;20         res.redirect(‘/home‘);21     } else {22         res.redirect(‘/login‘);23     }24 });25 26 router.get(‘/logout‘, function(req, res) {27     req.session.user = null;28     res.redirect(‘/‘);29 });30 31 router.get(‘/home‘, function(req, res) {32     res.render(‘home‘, { title: ‘Home‘ });33 });34 35 module.exports = router;

  本地安装数据库MongoDB,新建用户nodedb。重启服务,访问成功。


  8、页面访问控制及提示

  访问控制设计:

访问路径描述
/任何人都可以访问,不需要认证。
/home拦截get请求,调用authentication()进行认证,不通过则自动跳转到登录页面。
/login任何人都可以访问,不需要认证。
/logout任何人都可以访问,不需要认证。

  修改index.js文件:

34 router.get(‘/home‘, function(req, res) {35     authentication(req, res);36     res.render(‘home‘, { title: ‘Home‘ });37 });38 39 function authentication(req, res) {40     if (!req.session.user) {41         return res.redirect(‘/login‘);42     }43 }

  重启服务,访问成功。

  添加页面提示,修改app.js文件,增加res.locals.message:

40 app.use(function(req, res, next) {41     res.locals.user = req.session.user;42     var err = req.session.error;43     delete req.session.error;44     res.locals.message = ‘‘;45     if (err) {46         res.locals.message = ‘<div class="alert alert-warning">‘ + err + ‘</div>‘;47     }48     next();49 });

  修改index.js文件,增加req.session.error:

 1 var express = require(‘express‘); 2 var router = express.Router(); 3  4 /* GET home page. */ 5 router.get(‘/‘, function(req, res) { 6     res.render(‘index‘, { title: ‘Express‘ }); 7 }); 8  9 router.route(‘/login‘)10 .get(function(req, res) {11     if (req.session.user) {12         res.redirect(‘/home‘);13     }14     res.render(‘login‘, { title: ‘用户登录‘ });15 })16 .post(function(req, res) {17     var user = {18         username: ‘admin‘,19         password: ‘123456‘20     }21     if (req.body.username === user.username && req.body.password === user.password) {22         req.session.user = user;23         res.redirect(‘/home‘);24     } else {25         req.session.error=‘用户名或密码不正确‘;26         res.redirect(‘/login‘);27     }28 });29 30 router.get(‘/logout‘, function(req, res) {31     req.session.user = null;32     res.redirect(‘/‘);33 });34 35 router.get(‘/home‘, function(req, res) {36     authentication(req, res);37     res.render(‘home‘, { title: ‘Home‘ });38 });39 40 function authentication(req, res) {41     if (!req.session.user) {42         req.session.error=‘请先登录‘;43         return res.redirect(‘/login‘);44     }45 }46 47 module.exports = router;

  修改login.html,增加<%- message %>:

5 <legend>用户登录</legend>6 <%- message %>7 <div class="form-group">

  重启服务,访问成功。输入错误用户名密码:

图1 登录不成功提示