Nodejs 实现基于 Token 的认证应用

Nodejs 实现基于 Token 的认证应用

Posted by 刘一凡 on June 23, 2017

推荐阅读

JWT

JWT 代表 JSON Web Token ,它是一种用于认证头部的 token 格式。这个 token 帮你实现了在两个系统之间以一种安全的方式传递信息。

实例

我们一般不直接使用 MongoDB 的函数操作数据库,而使用 Mongoose 来操作 MongoDB(参考手册)。

创建用户 Model

src/models/user.js

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var bcrypt = require('bcrypt');     // 第三方的密码加密库

var UserSchema = new Schema({
  name: {
    type: String,
    unique: true, // 是否唯一
    require: true, // 是否为空
  },
  password: {
    type: String,
    require: true
  },
  token: {
    type: String
  }
})

// 添加用户保存时中间件对password进行bcrypt加密,这样保证用户密码只有用户本人知道
UserSchema.pre('save', function (next) {
  var _this = this

  if (this.isModified('password') || this.isNew) {
    bcrypt.genSalt(10, function (err, salt) {
      if (err) {
        return next(err)
      }
      bcrypt.hash(_this.password, salt, function (err, hash) {
        if (err) {
          return next(err)
        }
        _this.password = hash
        next()
      })
    })
  } else {
    return next()
  }
})

// 校验用户输入密码是否正确
UserSchema.methods.comparePassword = function (passw, cb) {
  bcrypt.compare(passw, this.password, function (err, isMatch) {
    if (err) {
      return cb(err)
    }
    cb(null, isMatch)
  })
}

var UserModel = mongoose.model('User', UserSchema)


module.exports = UserModel

链接数据库

src/app.js

var mongoose = require("mongoose");
var config = require('./config/default');

mongoose.connect(config.db); // 连接数据库

src/config/default

module.exports = {
  'secret': 'LyfAccoutApp',
  'db': 'mongodb://localhost:27017/accoutapp'
}

配置 HTTP 请求

我们需要允许不同网站的请求,解决跨域请求的错误。

  • Access-Control-Allow-Origin 允许所有的域名
  • 你可以向这个设备发送 POSTGET 请求
  • 允许 X-Requested-Withcontent-type 头部

src/app.js

app.use(function(req, res, next) {
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST');
    res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type, Authorization');
    next();
});

路由处理

src/app.js

var index = require('./routes/index');
var user = require('./routes/user');

app.use('/', index);
app.use('/api/user', user);

src/routes/user.js

var express = require('express')
var User = require('../models/user')
var jwt = require('jsonwebtoken')
var config = require('../config/default')
var router = express.Router();


// 注册用户
router.post('/signup', function (req, res) {
  if (!req.body.name || !req.body.password) {
    res.json({
      success: false,
      message: '请输入您的账号密码。'
    })
  } else {
    var newUser = new User({
      name: req.body.name,
      password: req.body.password
    })

    // 保存用户账号
    newUser.save(function (err) {
      if (err) {
        return res.json({
          success: false,
          message: '注册失败!'
        })
      }
      res.json({
        success: true,
        message: '成功创建新用户!'
      })
    })
  }
})

// 检查用户名和密码生成一个 accesstoken 如果验证通过
router.post('/accesstoken', function (req, res) {
  User.findOne({
    name: req.body.name
  }, function (err, user) {
    if (err) {
      throw err;
    }

    // 用户不存在
    if (!user) {
      res.json({
        success: false,
        message: '认证失败,用户不存在!'
      })
    } else if (user) {
      // 检查密码是否正确
      user.comparePassword(req.body.password, function (err, isMatch) {
        if (isMatch && !err) {
          var token = jwt.sign(
            {
              name: user.name
            },
            config.secret,
            {
              expiresIn: 3600
            }
          )

          user.token = token
          user.save()

          res.json({
            success: true,
            message: '验证成功!',
            token: 'Bearer ' + token,
            name: user.name
          })
        } else {
          res.json({
            success: false,
            message: '认证失败,密码失败!'
          })
        }
      })
    }
  })
})

// 获取用户信息
router.get('/info', ensureAuthorized, function(req, res) {
    User.findOne({token: req.token}, function(err, user) {
        if (err) {
            res.json({
                success: false,
                data: "Error occured: " + err
            });
        } else {
            res.json({
              username: user.name
            });
        }
    });
});

// 验证 Authorization
function ensureAuthorized (req, res, next) {
  var bearerToken;
  var bearerHeader = req.headers['authorization']
  console.log(req.headers)
  if (typeof bearerHeader !== 'undefined') {
    var bearer = bearerHeader.split(" ")
    bearerToken = bearer[1]
    req.token = bearerToken
    next()
  } else {
    res.status(401).json({
      success: false,
      message: 'Unauthorized'
    })
  }
}

module.exports = router;