1. 程式人生 > >node試水——express+mlab登入註冊

node試水——express+mlab登入註冊

express+mlab登入註冊

簡介和思路

  • 使用express作為node框架,mlab作線上資料庫,postman作為除錯介面工具;
  • 註冊時匹配唯一郵箱,錄入使用者名稱、密碼(用bcrypt加密過)、郵箱、頭像(獲取gravatar公認頭像),儲存資料庫成功後返回使用者資訊;
  • 登入時匹配使用者名稱和密碼,匹配成功後返回Bearer token令牌;
  • 訪問一個介面,校驗傳入的authorization是否和登入時的token匹配,匹配通過則返回介面資料,否則響應未通過認證。

github

檔案目錄

檔案目錄

流程演示

註冊成功後返回使用者資訊:
使用者資訊
註冊時郵箱重複後返回錯誤資訊:
錯誤資訊
登入成功後返回token:
token


密碼錯誤返回錯誤資訊:
錯誤資訊
訪問一個返回使用者資訊的介面,token驗證通過才能獲取到:
使用者資訊
token驗證不通過則預設會返回驗證失敗:
驗證失敗

用到的依賴和工具

  • 工具:postman 傳送請求和接收響應,用於調介面
  • “bcrypt”: “^3.0.2” 對註冊密碼進行加密
  • “body-parser”: “^1.18.3” 對post請求的請求體進行解析
  • “express”: “^4.16.4” nodejs框架,設定中介軟體響應HTTP請求,定義路由執行HTTP請求動作,給HTML頁面傳遞引數
  • “gravatar”: “^1.6.0” 全球公認頭像
  • “jsonwebtoken”: “^8.3.0” 跨域認證解決方案,用於生成token令牌,服務端認定使用者身份
  • “mongoose”: “^5.3.8” 對mongodb進行便捷操作的物件模型工具
  • “passport”: “^0.4.0” 用於驗證登入資訊,如果token通過才能訪問介面
  • “passport-jwt”: “^4.0.0”

server.js

功能:配置/連線資料庫,配置訪問API的中介軟體,配置/監聽埠號等等。

const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const passport = require(
'passport'); // 介面api const users = require('./routers/api/users'); const profiles = require('./routers/api/profiles'); const app = express(); // 使用body-parser中介軟體 app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); // 使用中介軟體使用users,訪問/api/users時獲取到users app.use('/api/users', users); app.use('/api/profiles', profiles); // 連結資料庫 const db = require('./config/keys').mongoURL; mongoose .connect( db, { useNewUrlParser: true } ) .then(() => { console.log('connect'); }) .catch(err => { console.log(err); }); // 初始化passport // passport的配置 app.use(passport.initialize()); require('./config/passport')(passport); // 訪問主頁時給頁面傳送資料 app.get('/', (req, res) => { res.send('hello world'); }); const port = process.env.PORT || 5000; app.listen(port, () => { console.log('server start'); });

users.js

功能:介面程式碼,配置router

const express = require('express');
const router = express.Router();
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const gravatar = require('gravatar');
const keys = require('../../config/keys');
const passport = require('passport');

const User = require('../../models/User');

// $router POST api/users/register
// @desc 返回請求的json資料
// @access public
router.post('/register', (req, res) => {
 // 查詢一條記錄
 User.findOne({
  email: req.body.email
 }).then(user => {
  if (user) {
   return res.status(400).json('郵箱已被註冊');
  } else {
   var avatar = gravatar.url('req.body.email', { s: '200', r: 'pg', d: 'mm' });
   const newUser = new User({
    avatar,
    name: req.body.name,
    email: req.body.email,
    password: req.body.password,
    identity: req.body.identity
   });

   // 用bcrypt對密碼進行加密
   bcrypt.genSalt(10, function(err, salt) {
    bcrypt.hash(newUser.password, salt, (err, hash) => {
     if (err) {
      throw err;
     }
     // hash為加密後的密碼
     newUser.password = hash;
     // 呼叫儲存方法
     newUser
      .save()
      .then(user => {
       res.json(user);
      })
      .catch(err => {
       console.log(err);
      });
    });
   });
  }
 });
});

// $router POST api/users/login
// @desc 返回token jwt password
// @access public
router.post('/login', (req, res) => {
 const email = req.body.email;
 const password = req.body.password;
 User.findOne({ email }).then(user => {
  if (!user) {
   return res.status(404).json('使用者不存在');
  }
  // 密碼匹配
  bcrypt.compare(password, user.password).then(isMatch => {
   if (isMatch) {
    // 規則,加密名字,過期時間,回撥
    const rule = {
     id: user.id,
     name: user.name,
     avatar: user.avatar,
     identity: user.identity
    };
    // 登入成功後返回token,相當於一個令牌,只有令牌校驗成功,才可以獲取想要的資料
    jwt.sign(rule, keys.secretOrKey, { expiresIn: 3600 }, (err, token) => {
     if (err) {
      throw err;
     }
     res.json({
      success: true,
      // 必須加Bearer
      token: 'Bearer ' + token
     });
    });
    // res.json({ msg: 'success' });
   } else {
    return res.status(404).json('密碼錯誤');
   }
  });
 });
});

// $router GET api/users/current
// @desc 返回current user
// @access private
router.get('/current', passport.authenticate('jwt', { session: false }), (req, res) => {
 res.json({
  id: req.user.id,
  name: req.user.name,
  email: req.user.email,
  identity: req.user.identity
 });
});

module.exports = router;

User.js

功能:配置mongoose的模型

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

// 建立模型和所需要的欄位
const UserSchema = new Schema({
 name: {
  type: String,
  required: true
 },
 email: {
  type: String,
  required: true
 },
 password: {
  type: String,
  required: true
 },
 avatar: {
  type: String
 },
 identity: {
  type: String,
  required: true
 },
 data: {
  type: Date,
  default: Date.now
 }
});

module.exports = User = mongoose.model('users', UserSchema);

passport.js

const JwtStrategy = require('passport-jwt').Strategy,
 ExtractJwt = require('passport-jwt').ExtractJwt;
const mongoose = require('mongoose');
const User = mongoose.model('users');
const keys = require('../config/keys');

const opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = keys.secretOrKey;

// passport驗證token
module.exports = passport => {
 passport.use(
  new JwtStrategy(opts, (jwt_payload, done) => {
   User.findById(jwt_payload.id)
    .then(user => {
     if (user) {
      return done(null, user);
     } else {
      return done(null, false);
     }
    })
    .catch(err => {
     console.log(err);
    });
  })
 );
};

keys.js

module.exports = {
//註冊mlab之後,create生成的地址
 mongoURL: 'mongodb://manager-web:[email protected]:47233/resful-api-prod',
 secretOrKey: 'secret'
};