使用express搭建一个在线便利贴

Express

基于Node.js 平台,快速、开放、极简的 web 开发框架。

  • Express使用文档

  • 新建项目文件夹

      mkdir express-node
    
  • 初始化项目

      //因为是node项目,需要初始化一下,生成package.json文件
      npm init -y  
      // -y 表示使用默认,和无脑回车相同。
    
  • 安装express

      npm install express --save
    
  • 切换镜像源小工具

      npm install nrm    //解决安装过慢问题
      nrm ls  //展示所有镜像源
      例: nrm use taobao  //切换淘宝镜像源
      // 如果自己发布包到npm,需要再切换回来
    
  • Express 应用生成器
    Express 应用生成器-文档

      npm install express-generator --save-dev   //不需要全局,有的机器会需要权限
    
     全局安装了 express 运行
     未全局安装 ./node_modules/express-generator/bin/express-cli.js  运行
    
     ./node_modules/express-generator/bin/express-cli.js ./ -f -e
     //在当前目录下强制创建ejs模板引擎  -h 查看所有模板引擎
     //会创建文件夹:bin、public、routes、views
    
     提示安装依赖
     install dependencies:  $ cd ./ && npm install
     npm install  //安装依赖
    
  • 启动服务器

      方法一: npm start  // 启动服务器 ==>浏览器打开 http://localhost:3000/  
      方法二: node bin/www
    
  • 改变端口号

    // 因为npm start  才可以启动服务器,
    在package.json文件中 start: "node ./bin/www"   
    在bin/www中 port = normalizePort(process.env.PORT || '3000');
    3000就是端口号
    或者启动的时候 PORT=4000 node bin/www  启动端口为4000
    80端口是默认的端口,如果使用需要权限,需要再前面加 sudo
    window 需要权限,需使用管理员身份运行
    
  • app.js

      app.set('views', path.join(__dirname, 'views'));
      // 把views设置为当前模板路径
      app.set('view engine', 'ejs');
      // 模板引擎 ejs
    
      // 中间键
      app.use(express.static(path.join(__dirname, 'public'))); 
      不走路由(routes文件夹),直接从public中获取
    
      //路由
      app.use('/', index);
      app.use('/users', users);
    
  • MVC
    控制路由跳转的就是控制器
    视图 views 的模板 就是V
    通过C去控制请求的流向,通过M去获取数据,通过V去把数据渲染好,展示给用户。

  • Express

  • 静态页面

      public文件夹中的静态文件,
      app.use(express.static(path.join(__dirname, 'public')));
      static 设置静态资源,从public中查找对应文件夹,根据文件夹目录进行查找
      如果找到了,就表示要的是文件,就把文件传输过来,不再往下查找
    
      例:注释app.use(express.static(path.join(__dirname, 'public')));
      app.use('/stylesheets/style.css',function(req,res(){
        res.send('get style.css...')
      }))
      //就会走路由,把get style.css...传到页面。不注释,就会传文件。
    

    换句话说:用户请求的任何东西,对这个网站来说都是路由,static是专门对静态资源进行的拦截,发现路径和文件夹匹配上了,传输文件。所以,后缀名没有任何意义,只是给用户分辨而已。

  • 新建src 文件夹
    目的,使用webpack编译到public文件夹中,显示给用户

  • 搭建简单测试环境


    纠正:webpack.config.js放在src下
     index.js
     var obj = require('../mod/b.js')
     console.log(obj)
    
     a.js
     module.exports.a='aaaaa'
    
     b.js
     var a = require('./a.js').a
     module.exports = {
       b: 'bbbb',
       a: a
     }
    
     webpack.config.js
     var webpack = require('webpack')
     var path = require('path')
     module.exports = {
         entry: path.join(__dirname,"js/app/index.js"),
         output:{
             path: path.join(__dirname,"../public/js"),
             filename: "index.js"
         }
     }
    
  • webpack压缩

     安装webpack
     npm install webpack --save-dev
     在package.json的script中添加
     "webpack":"webpack --config=src/webpack.config.js"
     npm run webpack
     压缩路径public/js/index.js,检查文件代码是否压缩成功
    
  • 自动上传小工具

     每次执行npm run webpack 太麻烦,使用自动上传小工具,每次更改自动上传
     npm install --save-dev onchange
     在package.json的script中添加
     "watch": "onchange src/**/*.js src/**/*.less -- npm run webpack "
     npm run watch 运行自动压缩
    
  • 切换node.js版本小工具

     npm install -g n
     n 6.10.0  切换版本
    
  • 新建toast板块,提示便利贴增删改查状态

    • src --> mod --> toast.js
     function toast(msg,time){
      this.msg = msg;
      this.dismissTime = time||1000,
      this.createToast();
      this.showToast();
    }
    toast.prototype = {
      createToast: function(){
          var tpl = '<div class="toast">'+this.msg+'</div>';
          this.$toast = $(tpl);
          $('body').append(this.$toast);
      },
      showToast:function(){
          var self = this;
          this.$toast.fadeIn(300,function(){
              setTimeout(function(){
                  self.$toast.fadeOut(300,function(){
                      self.$toast.remove();
                  });
              },self.dismissTime);
          })
      }
    }
    
    function Toast(msg,time){
        return new toast(msg,time);
    }
    
    Toast('hello')
    
    module.exports.Toast = Toast;
    
    • src --> less --> toast.js
     .toast{
      position: fixed;
      left: 50%;
      transform: translateX(-50%);
      bottom: 20px;
      color: #D15A39;
      background: #fff;
      padding: 5px 10px;
      border-radius: 3px;
      box-shadow: 0px 0px 3px 1px rgba(255,255,255,0.6);
      display: none;
    }
    
  • LESS
    我的博客
    LESS中文文档

    新建text.html

  • 运行toast

      删除a.js b.js 
      index.js ===>
      var Toast = require('../mod/toast.js').Toast;
      Toast('hello world')
      在src ==> js ==> lib ==>添加jQuery
      在toast.js中添加
      var $ = require('../lib/jquery.min.js') 引用jQuery
    
      简化引入jQuery代码
      在webpack.config.js中添加
      resolve:{
          alias:{ //当require引入模块的时候,简化路径
              jquery: path.join(__dirname,"js/lib/jquery.min.js"),
              mod: path.join(__dirname,"js/mod"),
              less: path.join(__dirname,"less")
          }
      },
    
      toast.js代码var $ = require('../lib/jquery.min.js') 
      改成 var $ = require('jquery') 
    
      在webpack.config.js中添加
      plugins: [ //所有页面都可以使用jQuery
          new webpack.ProvidePlugin({ 
              $: 'jquery'
          }),
      ]
      toast.js代码var $ = require('jquery') 删除也可以使用jQuery了。
    
      在toast.js中添加require('less/toast.less'),使用less
      在webpack.config.js中添加
      module:{
          rules: [
              {  //当require一个东西的时候,会进行检测
                  test: /\.less$/,  //正则表达式,以less为后缀
                  use: ["style-loader","css-loader","less-loader"]
                  //使用这些loader,向前解析 less解析成css,放到页面上
              }
          ]
      },
      npm install --save css-loader less-loader style-loader less 安装
      使用less
    
  • 新建event.js(src ==> js ==> mod == event.js)

    // 设计模式:发布订阅模式;用于组件之间进行一个解耦
    
    var EventCenter = (function(){
        var event = {};
        function on(evt,handler){
            events[evt] = events[evt] || [];
    
            events[evt].push({
                handler: handler
            });
        }
    
        function fire(evt,args){
            if(!events[evt]){
                return
            }
            for(var i = 0;i>events[evt].length;i++){
                events[evt][i].handler(args);
            }
        }
    
        return {
            on: on,
            fire:fire
        }
      })();
    
  • 新建waterfall.js(src ==> js ==> mod == waterfall.js)

      //瀑布流布局
      var WaterFall = (function(){
      var $ct;
      var $items;
    
      function render($c){
          $ct = $c;
          $items = $ct.children();
    
          var nodeWidth = $items.outerWidth(true),
            colNum = parseInt($(window).width()/nodeWidth),
            colSumHeight = [];
    
          for(var i = 0 ;i<colNum;i++){
              colSumHeight.push(0);
          }
    
          $items.each(function(){
              var $cur = $(this);
    
              // colSumHeight = [100, 250, 80 , 200]
    
              var idx = 0,
                  minSumHeight = colSumHeight[0];
    
              for(var i = 0;i<colSumHeight.length;i++){
                  if(colSumHeight[i] < minSumHeight){
                      idx = i;
                      minSumHeight = colSumHeight[i];
                  }
              }
    
              $cur.css({
                  left: nodeWidth*idx,
                  top: minSumHeight
              });
              colSumHeight[idx] = $cur.outerWidth(true) + colSumHeight[idx];
          });
      }
    
      $(window).on('resize',function(){
          render($ct);
      })
    
      return{
          init: render
      }
    })()
    
    // WaterFall.init($('#content'))
    
    module.exports = WaterFall
    
  • 新建note.js (src ==> js ==> mod == note.js)

    require('less/note.less');
    //拿到样式
    
    /*
    { id: ''; text: 'hello';}
    */
    var Toast = require('./toast.js').Toast;
    var Event = require('mod/event.js');
    
    function Note(opts){
      this.initOpts(opts);
      this.createNote();  //创建便利贴
      this.setStyle();  //设置样式
      this.bindEvent();
    }
    Note.prototype = {
      colors: [
        ['#ea9b35','#efb04e'], // headColor, containerColor
        ['#dd598b','#e672a2'],
        ['#eee34b','#f2eb67'],
        ['#c24226','#d15a39'],
        ['#c1c341','#d0d25c'],
        ['#3f78c3','#5591d2']
      ],
    
      defaultOpts: {
        id: '',   //Note的 id
        $ct: $('#content').length>0?$('#content'):$('body'),  
        //默认存放Note 的容器
        context: 'input here'  //Note 的内容
      },
    
      initOpts: function (opts) { //默认一个初始化,设置id和参数
        this.opts = $.extend({}, this.defaultOpts, opts||{});
        if(this.opts.id){
           this.id = this.opts.id;
        }
      },
    
      createNote: function () {
        var tpl =  '<div class="note">'
                + '<div class="note-head"><span class="delete">&times;</span></div>'
                + '<div class="note-ct" contenteditable="true"></div>'
                +'</div>';
        // 创建html,放到网页上
        // contenteditable  不是input,设置H5属性,可以编辑
        this.$note = $(tpl);
        this.$note.find('.note-ct').html(this.opts.context);
        this.opts.$ct.append(this.$note);
        if(!this.id)  this.$note.css('bottom', '10px');  //新增放到右边
      },
    
      setStyle: function () {
        var color = this.colors[Math.floor(Math.random()*6)];
        this.$note.find('.note-head').css('background-color', color[0]);
        this.$note.find('.note-ct').css('background-color', color[1]);
      },
    
      setLayout: function(){
        var self = this;
        if(self.clk){
          clearTimeout(self.clk);  
        }
        self.clk = setTimeout(function(){
          Event.fire('waterfall');  //发送一个瀑布流
        },100);
      },
    
      bindEvent: function () {
        var self = this,
            $note = this.$note,
            $noteHead = $note.find('.note-head'),
            $noteCt = $note.find('.note-ct'),
            $delete = $note.find('.delete');
    
        $delete.on('click', function(){
          self.delete();  //调用删除
        })
    
      //contenteditable没有 change 事件,所有这里做了模拟通过判断元素内容变动,执行 save
      $noteCt.on('focus', function() {
        if($noteCt.html()=='input here') $noteCt.html('');
        $noteCt.data('before', $noteCt.html());
      }).on('blur paste', function() {
        if( $noteCt.data('before') != $noteCt.html() ) {
          $noteCt.data('before',$noteCt.html());
          self.setLayout();
          if(self.id){
            self.edit($noteCt.html())
          }else{
            self.add($noteCt.html())
          }
        }
      });
    
      //设置笔记的移动
      $noteHead.on('mousedown', function(e){  // 当鼠标点下去的时候
        var evtX = e.pageX - $note.offset().left,   
        //evtX 计算事件的触发点在 dialog内部到 dialog 的左边缘的距离
            evtY = e.pageY - $note.offset().top;
        $note.addClass('draggable').data('evtPos', {x:evtX, y:evtY}); 
        //把事件到 dialog 边缘的距离保存下来
      }).on('mouseup', function(){  // 鼠标移开的时候,把class删除掉
         $note.removeClass('draggable').removeData('pos');
      });
    
      $('body').on('mousemove', function(e){
        $('.draggable').length && $('.draggable').offset({
          top: e.pageY-$('.draggable').data('evtPos').y,   
          // 当用户鼠标移动时,根据鼠标的位置和前面保存的距离,计算 dialog 的绝对位置
          left: e.pageX-$('.draggable').data('evtPos').x
        });
      });
    },
    
    edit: function (msg) {  //当需要编辑的时候
      var self = this;
      $.post('/api/notes/edit',{
          id: this.id,
          note: msg
        }).done(function(ret){
        if(ret.status === 0){
          Toast('update success');
        }else{
          Toast(ret.errorMsg);
        }
      })
    },
    
    add: function (msg){
      console.log('addd...');
      var self = this;
      $.post('/api/notes/add', {note: msg})
        .done(function(ret){
          if(ret.status === 0){
            Toast('add success');
          }else{
            self.$note.remove();
            Event.fire('waterfall')
            Toast(ret.errorMsg);
          }
        });
      //todo
    },
    
    delete: function(){
      var self = this;
      $.post('/api/notes/delete', {id: this.id})
        .done(function(ret){
          if(ret.status === 0){
            Toast('delete success');
            self.$note.remove();
            Event.fire('waterfall')
          }else{
            Toast(ret.errorMsg);
          }
      });
    
    }
    
    };
    
    module.exports.Note = Note;
    
  • 新建note-manager.js(src ==> js ==> mod == note-manager.js)

    // 获取数据,添加数据
    var Toast = require('./toast.js').Toast;
    var Note = require('./note.js').Note;
    var Toast = require('./toast.js').Toast;
    var Event = require('mod/event.js');
    
    
    var NoteManager = (function(){
    
      function load() {
        $.get('/api/notes')
          .done(function(ret){
            if(ret.status == 0){
              $.each(ret.data, function(idx, article) {
                  new Note({
                    id: article.id,
                    context: article.text
                  });
              });
    
              Event.fire('waterfall');
            }else{
              Toast(ret.errorMsg);
            }
          })
          .fail(function(){
            Toast('网络异常');
          });
    
    
      }
    
      function add(){
        new Note();
      }
    
      return {
        load: load,
        add: add   //获取数据,和添加
      }
    
    })();
    
    module.exports.NoteManager = NoteManager
    
  • 修改index.js (src ==> js ==> app == index.js)

    清空测试代码,添加以下
    require('less/index.less');
    
    var NoteManager = require('mod/note-manager.js').NoteManager;
    var Event = require('mod/event.js');
    var WaterFall = require('mod/waterfall.js');
    
    NoteManager.load();  //加载所有的数据
    
    $('.add-note').on('click', function() {
      // 点击添加按钮,调用添加
      NoteManager.add();
    })
    
    Event.on('waterfall', function(){
      //事件听到waterfall的时候,执行一次瀑布流
      WaterFall.init($('#content'));
    })
    
  • 添加index.less

  • 约定接口

      1.获取所有的note: GET  /api/notes  req:{}  res:{ status: 0, data:[{},{}]}
      {status: 1,errorMsg:'失败的原因'}
      2.创建一个note:POST: /api/notes/add req:{note: 'hello world'}  res:{ status:0}
      {status: 1,errorMsg:'失败的原因'}
      3.修改一个note:POST:/api/notes/edit req:{note: 'new note',id:100}
      4.删除一个note:POST:/api/notes/detele req:{id:100}
    
      app.js 添加 app.use('/api',api)
      routes中api.js添加以上接口
      router.get('/notes', function(req, res, next) {
        console.log('/notes')
      });
    
      router.post('/notes/add', function(req, res, next) {
        var note = req.body.note;
        console.log('add...')
      });
    
      router.post('/notes/edit', function(req, res, next) {
    
      });
    
      router.post('/notes/delete', function(req, res, next) {
    
      });
    
  • 数据库
    SQL 教程
    npm mysql
    sequelize
    sequelize文档 V3
    npm install --save sequelize

     npm install --save sqlite3
     如果node-v48-win32-x64.tar.gz卡住了,赋值url,fq下载,
     替换到node_modules/sqlite3/lib/binding 里面的 node-v48-linux-x64
    
      使用sequelize
      var Sequelize = require('sequelize');
      var sequelize = new Sequelize('database', 'username', 'password');
      // 连接数据库
      
      //定义一个表,就是一个模型,对应数据库里一个表
      var User = sequelize.define('user', { // 对应数据库里,user表
        username: Sequelize.STRING,   //string
        birthday: Sequelize.DATE    //date
      });
    
      sequelize.sync().then(function() {  //创建这个表
        return User.create({    //创建一个数据
          username: 'janedoe',
          birthday: new Date(1980, 6, 20)
        });
      }).then(function(jane) {
        console.log(jane.get({
          plain: true
        }));
      });
    
  • node端如何调试

      npm install -g node-inspector 安装调试工具
      node-inspector  启动调试软件(默认占用8080端口)  
      // 注意提示调试网址,通过该网址进行调试
      还需要开启服务器    node --debug bin/www
      刷新服务器网址,再刷新调试网站即可。
    
  • 检测数据能否使用

      在项目目录下新建model ==> note.js
      var sequelize = new Sequelize(undefined, undefined, undefined, {  
        //不需要用户名密码,如果是其他数据库需要做义工配置
        host: 'localhost',
        dialect: 'sqlite',
        storage: '../database/database.sqlite'
      });
      //测试是否成功,使用完成就可注释掉
      sequelize
      .authenticate()
      .then(function(err){
        console.log('Connection has been established successfully.');
      })
      .catch(function(err){
        console.log('Unable to connect to the database:',err);
      })
       
      routes ==》 aip.js
      var Note = require('../model/note')
      router.get('/notes', function(req, res, next) {
        var data = Note.getAll()  //模型,用于具体的和数据进行操作。
        //假设有个对象叫Note,有getAll方法,把他所有数据赋值给data
        res.send({status: 0, data: notes})
      });
      cd model
      node note.js  会在database文件夹下生成空的database.sqlite
    
  • 向数据库添加数据

      note.js添加数据,创建模型,一个模型,对应数据库的一个表
      // id  [字段]  [添加时间] [更新时间]
      //模型起名叫note
      var Note = sequelize.define('note', {
        text: {
          type: Sequelize.STRING   
          //向数组添加字段,类型为string,会默认添加id 添加时间 更新时间
        }
      });
    
    
      //sync({force: true}) 假设数据库存在这个表,force:true删除。
      Note.sync().then(function(){
         Note.create({text:'hello world'})
      }).then(function(){
        Note.findAll().then(function (notes) {   
        //findOne({raw: true}) 获取里面的数据
          console.log(notes);
        })
      })
      //添加完成后注释
      
      cd model  运行 node note.js
      Note.findOne({raw: true, where:{id:2}}).then(function(notes){
        console.log(notes)
      })
      //findOne({raw: true}) 获取里面的数据,如果不加,会把所有数据展示
      // 会输出 id 为 2 的数据。
    
  • api.js中的数据增删改查

      router.post('/notes/add', function(req, res, next) {
      if(!req.session || !req.session.user){
        return res.send({status: 1, errorMsg: '请先登录'})
      }
      var uid = req.session.user.id
      var note = req.body.note;
    
      Note.create({text: note, uid: uid}).then(function(){
        res.send({status: 0})  //status: 0 成功
      }).catch(function(){
        res.send({status: 1,errorMsg: '数据库出错'}) //status: 1 出错
      })
      console.log('add...',note)
    })
    
    router.post('/notes/edit', function(req, res, next) {
      if(!req.session || !req.session.user){
        return res.send({status: 1, errorMsg: '请先登录'})
      }
      var uid = req.session.user.id
      Note.update({text: req.body.note},{where:{id:req.body.id,uid:uid}}).then(function(){
        console.log(arguments)
        res.send({status:0})
      }).catch(function(){
        res.send({status: 1,errorMsg: '数据库出错'}) //status: 1 出错
      })
    });
    
    router.post('/notes/delete', function(req, res, next) {
      if(!req.session || !req.session.user){
        return res.send({status: 1, errorMsg: '请先登录'})
      }
      var uid = req.session.user.id
      Note.destroy({where:{id:req.body.id,uid:uid}}).then(function(){
        res.send({status:0})
      }).catch(function(){
        res.send({status: 1,errorMsg: '数据库出错'}) //status: 1 出错
      })
    });
    
  • 修改路径

      使用更稳定的路径方式
      model ==》 note.js
      var path = require('path')  //添加到页面
      storage: path.join(__dirname,'../database/database.sqlite') //修改页面代码
      module.exports.Note = Note;  //导出,提供给其外部使用
      
      routes ==> api.js
      var Note = require('../model/note').Note; //修改代码,引用note.js
    
  • 登录功能
    阮一峰 oauth 2.0文章
    oauth 2是一个认证协议。当点击第三方登录的时候,跳转到第三方网站,登录成功后,返回当前页面。中间oauth2.0做一个支撑。(点击登录,向后台发起请求,后台向第三方接口请求,登录成功,网站后台拿到一个key,返回页面。)

    • app.js增加第三方登录接口
      var auth = require('./routes/auth');
      app.use('/auth', auth);
    
    • 增加auth路由
      routes ==> 新建auth.js
    
      var passport = require('passport');
      var session = require('express-session');
    
      app.use(session({secret: '[随便写一串字符串,作为一个秘钥]'}));
      app.use(passport.initialize());
      app.use(passport.session());
    
  • cookie 和 session的关系
    http是一个无状态的协议,每次请求不知道是谁发的请求,只知道有一个请求url过来,需要做什么事情。
    需求:用户登录了之后,刷新了页面,下次还是登录状态。需要记录用户的状态。
    这个时候,需要使用cookie和session,当使用用户名和密码提交给后台的时候,后台会从数据库里面查询,(一般密码都不是明文,都是经过MD5和SHA1加密的,会把密码从新进行一次加密。),加密好了之后,从数据库查询用户名,得到之后,再把加密后的密码进行匹配。如果匹配上了,就表示登录上了,表示登录上了也没用,所有需要记录登录状态,把用户的信息,再服务器里面创建一个session,一个session可以认为是一个数据对象,在内存里面,session有一个key,key对应的value就是session,session可以存储在任何地方,默认存在内存里,没有把他存在某个地方,是一个变量,存在服务器内存,key就是一串很长的数值。
    用户请求过来,向用户展示页面,在服务器通过send cookie,相当于http请求,当用户打开页面的时候,发现http的请求里面,有一个叫cookie,会把key存到cookie里面,里面的文件connect.sid传字符串。
    在刷新页面,就会把所有请求都带上,同时cookie也发到服务器里面,服务器就通过这个cookie,就从刚才的数据内存里面去查找,得到这个对象,然后得到这个用户,展示和这个用户相关的信息。
    session是服务端的对象,有个key,把key发给浏览器,浏览器记录下来,这就是cookie。

  • 添加登录注销效果

    • index.ejs
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <meta property="qc:admins" content="4562636714562571563145" />
        <title><%= title %></title>
        <link rel="stylesheet" href="/css/index.css">
      </head>
      <body>
        <div id="header">
          <a class="add-note" title="添加笔记" href="#"><span class="fa fa-plus"></span> 添加</a>
          <ul class="user-area">
           <% if (isLogin){ %>
              <li><img src="<%= user.avatar %>" alt=""></li>
              <li><span title="<%= user.username %>"><%= user.username %></span></li>
              <li><span class="line"> | </span> </li>
              <li><a class="logout" href="/auth/logout">注销</a></li>
            <%} else { %>
              <li><a class="login" title="GitHub登录" href="/auth/github"> GitHub登录</a>
              </li>
            <% } %>
          </ul>
        </div>
        <div id="content">
        </div>
        <div class="stars"></div>
        <script src="/js/index.js"></script>
    <!--     <div class="twinkling"></div> -->
      </body>
    </html>
    
    • index.js
    var express = require('express');
    var router = express.Router();
    /* GET home page. */
    router.get('/', function(req, res, next) {
      res.render('index', { title: '我的便利贴' });
    });
    router.get('/', function(req, res, next) {
      var data;
      if(req.session.user){
        data = {
          isLogin: true,
          user: req.session.user
        }
      }else{
        data = {
          isLogin: false
        }
      }
      console.log(data)
      res.render('index', data);
    });
    
    module.exports = router;
    
    • auth.js
    var express = require('express');
    var router = express.Router();
    
    var passport = require('passport');
    //引入passport,专门负责auth2的认证,所有的第三方登录,都可以使用passport
    var GitHubStrategy = require('passport-github').Strategy;
    //在passport基础上,对整个协议进行的封装。
    
    passport.serializeUser(function(user, done) {
      console.log('---serializeUser---')
      console.log(user)
      done(null, user);
    });
    //把用户登陆过来的信息,传递到passport之后,让它生成一个session,存储到内存里面
    
    passport.deserializeUser(function(obj, done) {
      console.log('---deserializeUser---')
      done(null, obj);
    });
    // 用户刷新页面的时候,会再从内存里面,把对应的session拿出来,解析,知道这个用户
    passport.use(new GitHubStrategy({
      clientID: '28928eae8774e53d2247',
      clientSecret: '0485fe61b10dac188ff17d977253ba091113f94d',
      callbackURL: "http://127.0.0.1:3000/auth/github/callback"
    },
    function(accessToken, refreshToken, profile, done) {
      // User.findOrCreate({ githubId: profile.id }, function (err, user) {
      // });
      done(null, profile);
    }
    ));
    //入口
    router.get('/github',
    passport.authenticate('github'));
    
    router.get('/github/callback',
    passport.authenticate('github', { failureRedirect: '/login' }),
    function(req, res) {
      req.session.user = {
        id: req.user.id,
        username: req.user.displayName || req.user.username,
        avatar: req.user._json.avatar_url,
        provider: req.user.provider
      };
      res.redirect('/');
    });
    
    //注销
    router.get('/logout',function(req,res){
      req.session.destroy()  //销毁session
      res.redirect('/')  //跳转到首页
    })
    
    module.exports = router;
    

npm start 开启本地服务器

推荐阅读更多精彩内容