从零开始用Node.js 和Mongodb 写一个JWT Auth Server - 用户认证服务器

房项禹
2023-12-01

最近学习React 自己的在线购物网站做的差不多了,需要开始用户登陆,生成订单,然后用palpay付款了。于是开始自己搭建一个Node.js 的后端服务器。

安装node.js mongodb 就不再说了,直接开始从代码开始说起吧

package.json 可以看出来要安装的包:

使用了以下包:

  • express  Web框架
  • mongoose 数据库连接库
  • morgan morgan是express默认的日志中间件
  • ody-parser 是非常常用的一个express中间件,作用是对post请求的请求体进行解析。
  • nodemon 可以动态修改代码应用node.js
  • bcrypt-nodejs 对于密码加密
  • jwt-simple 创建JWT
  • passport   验证登陆
  • passport-jwt  (看名字应该知道了,不用再说了吧)
  • passport-local  //通过用户名密码验证登陆
{
  "name": "server",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "nodemon index.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "bcrypt-nodejs": "0.0.3",
    "body-parser": "^1.19.0",
    "express": "^4.17.1",
    "jwt-simple": "^0.5.6",
    "mongoose": "^5.10.8",
    "morgan": "^1.10.0",
    "nodemon": "^2.0.4",
    "passport": "^0.4.1",
    "passport-jwt": "^4.0.0",
    "passport-local": "^1.0.0"
  }
}

下面建立程序。对于程序不再单独说明了,请看注解。

程序主文件

index.js

const express = require('express')
const http = require('http');
const bodyParser = require('body-parser');
const morgan = require('morgan');
const app = express();
const router = require('./router');
const mongoose = require('mongoose');

//DB Setup
mongoose.connect('mongodb://localhost:27017/auth',{useUnifiedTopology:true,useNewUrlParser:true}); 
//you have to change to your mongodb url


//App Setup
app.use(morgan('combined'));
app.use(bodyParser.json({type:'*/*'}));
router(app);



//Server Setup
const port = process.env.PORT || 3090;
const server = http.createServer(app);
server.listen(port);

console.log("Server listening on :",port);


config.js  // 这个文件是存本地密钥的,字符串是随便敲的,写什么都可以。一般正式的项目不要加入到git 里面上传,只放本地保存。

// Hold application secrets and config

module.exports = {
    secret:'a;lskdjf;laskjf;lskjewroiudf'
}

router.js // 路由文件可以根据自己需要继续扩展,我写了主页要求带上jwt访问的要求

const Authentication = require('./controllers/authentication');
const passportService = require('./services/passport');
const passport = require('passport');

const requireAuth = passport.authenticate('jwt',{session:false});
const requireSignin = passport.authenticate('local',{session:false});

module.exports = function (app) {
    app.get('/',requireAuth, (req,res) => {
        res.send({Login:'你已经登陆进来了!'});

    });
    app.post('/signin', requireSignin, Authentication.signin);
    app.post('/signup', Authentication.signup);

}

controllers/authentication.js 这里是控制器

const jwt = require('jwt-simple');
const User = require('../models/user');
const config = require('../config');

function tokenForUser(user) {
    const timestamp = new Date().getTime();

    return jwt.encode({sub: user.id, iat: timestamp}, config.secret);

}

exports.signin = function (req,res,next){
    //User has already had their email and password auth'd
    //We just need to give them a token
    res.send({token:tokenForUser(req.user)})
}


exports.signup = function (req, res, next) {

    const {email, password} = req.body;

    if (!email || !password) {
        return res.status(422).send({error: 'You must provide email and password '});
    }
    //See if a user with the given email exists
    User.findOne({email}, function (err, existingUser) {
        if (err) {
            return next(err);
        }
        //If a user with email does exist, return an error
        if (existingUser) {
            return res.status(422).send({error: 'Email is in use'});
        }

        //If a user with email does NOT exist, create and save user record
        const user = new User({email, password});
        user.save((err) => {
            if (err) {
                return next(err);
            }
            // Respond to request indicating the user was created
            console.log("now the user passwordis", user.password);
            res.json({token:tokenForUser(user)});
        });


    });

}

models/user.js 这里是用户类

const mongoose = require("mongoose");

const Schema = mongoose.Schema;
const bcrypt = require('bcrypt-nodejs');


//Define our model

const userSchema = new Schema({
    email: {type: String, unique:true, lowercase:true },
    password: String,
});

// On Save Hook, encrypt password;
//Before saving a model, run this function

userSchema.pre('save', function(next){ 
// HERE IS MUST NOT TO USING ARROW FUNCTION
    const user = this;

    //generate a salt then run callback
    bcrypt.genSalt(10, (err,salt) => {
        if(err){return next(err);}

        //hash(encrypt) our password using the salt
        bcrypt.hash(user.password,salt,null, (err,hash) => {
            if(err){return next(err);}

            //overwrite plain text password
            user.password = hash;
            next();
        })
    })
})

userSchema.methods.comparePassword =function (candidatePassword,callback) {
// HERE IS MUST NOT TO USING ARROW FUNCTION

    bcrypt.compare(candidatePassword,this.password, (err,isMatch) => {
        if(err){return callback(err);}

        callback(null,isMatch);

    })

}



//Create the model class
const ModelClass = mongoose.model('user',userSchema);


//Export the model
module.exports = ModelClass;

services/passport.js 这里是密码加密,数据库里存储的密码不可以是明码,就是这样

const passport = require('passport');
const User = require('../models/user');
const config = require('../config');
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const LocalStrategy  = require('passport-local');

// Create local strategy
const localOptions = {usernameField:'email'}
const localLogin = new LocalStrategy(localOptions, (email,password,done) => {
    // Verify this email and password, call done with the user
    // if it's the correct email and password
    // otherwise, call done with false


    User.findOne({email}, (err,user) => {
        if(err){ return done(err);}
        if(!user){return done(null,false);}

        //compare passwords - is password equal to user.password?
        user.comparePassword(password, (err,isMath) => {
            if(err){return done(err);}
            if(!isMath){return done(null,false);}

            return done(null,user);

        });
    });

})

// Setup options for JWT Strategy
const jwtOptions = {
    jwtFromRequest:ExtractJwt.fromHeader('authorization'),
    secretOrKey:config.secret
};


// Create JWT strategy
const jwtLogin = new JwtStrategy(jwtOptions, (payload,done) => {
    // See is the user ID in the payload exists in our database
    // if it does, call 'done' with that other
    // otherwise, call done without a user object
    User.findById(payload.sub, (err,user) => {
        if(err){return done(err,false);}

        if(user){
            done(null,user);
        }else{
            done(null,false);
        }

    })

})


// Tell passport to use this strategy
passport.use(jwtLogin);
passport.use(localLogin);

运行起来以后,就可以用postman来进行singup 然后复制得到的token ,访问 / 的时候再Headers 里面增加authorization,value复制token过来, 记得是get操作。

singup,singin 都是post操作,直接通过,http://localhost:3090/signin 和 http://localhost:3090/signup  send request, set  body ---> raw ---->  json

按照下面格式写

{
    "email":"1232TEST@abc.com",
    "password":"123123"
}

OK, 如果一切顺利,你已经做好一个非常安全的用户注册登陆api了。

 

 

 类似资料: