zoukankan      html  css  js  c++  java
  • 网站注册与登录使用 bcrypt与 passport 双重验证 解释

    网站在登录前,需要进行注册收集用户基本信息,bcrypt 提供密码加密验证的方法,但是使用不正确,会给初学者带来各种问题。

    bcrypt 的安装:

    npm i bcrypt

    经过测试,经常安装不成功,原因和node.js的版本有原因,我在 下面这篇文章中有记录解决办法:

    bcrypt 安装不成功解决办法

    但也不是万能的,如果还不能解决的话,可以尝试给 bcrypt 指定版本号安装:

    npm install --save bcrypt@2.0.1

    一般是会成功的!!

    用户在注册时,除了收集用户信息外,因为要用到 bcrypt ,必须要在注册时对密码进行加密,加密后再保存到数据库中。因为用户登录时,使用 bcrypt 的 compare 方法,这个方法是验证加密的密码的,如果在注册时没有加密,而登录时使用 compare 进行验证,直接会导致,将 mongodb 直接挂掉!而且登录也不会成功,也不报错,也不提示的这种尴尬的局面,会导致无从下手。

    bcrypt  注册的逻辑:

    1. 收集用户(表单)的基本信息
    2. bcrypt.genSalt() 给密码加密
    3. 加密完成,将数据保存在数据库
    router.post('/register',urlencodedParser,(req,res) => {
        //验证
        
        const newUser = new UserSchema({
            email: req.body.email,
            password:req.body.password,
            confirmPassword:req.body.confirmPassword,
            firstName:req.body.firstName,
            lastName:req.body.lastName
        });
    
        console.log('body:  ' + newUser)
    
        //给 newUser.password 加密,hash 为加密后的密码
        bcrypt.genSalt(10, function(err, salt) {
            bcrypt.hash(newUser.password, salt, (err, hash) => {
                if(err) throw err;
                
                newUser.password = hash;
    
                //保存到数据库
                newUser.save().then((user) => {
                    res.redirect('/admin/login');
                    res.send(user);
                }).catch((err) => {
                    res.render('/admin/register',{})
                })
            });
        });
    });
    res.redirect() 为跳转到哪个路径
    注册成功之后,直接跳转到 login 页面。

    登录相对比较复杂,会用到
    passport 与 bcrypt 进行验证。
    • passport 功能单一,支持本地账号验证和第三方账号登录验证,这里用到了本地用户登录验证。它本身不能作验证,它主要是用来验证请求的,是由 passport 与 local-passport 搭配使用的。
    • bcrypt 是单向的,跨平台的文件加密工具,经过它加密的密码,口令为8~56个字符,并在内容被转化为 448 位的密钥,在加密算法领域,越慢的加密算法越安全,bcrypt 比 md5 还要慢,因此它的算法是比较安全的,黑客破解成本高。因 bcrypt 是单向的,受攻击破解的概率大大降低。

    登录方法也可以不使用 passport 登录帐号验证,仅使用 bcrypt 进行密码验证:

    const express = require('express');
    const router = express.Router();
    const bodyParser = require('body-parser');
    const bcrypt = require('bcrypt');
    const urlencodedParser = bodyParser.urlencoded({ extended: false });
    
    require('../models/UserSchema');
    const UserSchema = mongoose.model('users');
    
    router.post("/login",urlencodedParser,(req,res,next) => {
        const loginUser = {
            email:req.body.email,
            password:req.body.password
        };
    
        console.log(loginUser);
    
    
         UserSchema.findOne({
             email:loginUser.email
           }).then(users => {if(!users){
               return done(null, false, {message: 'No User Found'});
            } 
           //验证密码
             bcrypt.compare(loginUser.password, users.password, (err, isMatch) => {
              if(err) throw err;
              if(isMatch){
               res.redirect('/')
             } else {
               res.redirect('/admin/login')
              }
            })
          })
      });

    同时使用 passport 与 bcrypt 进行验证:

    npm install passport --save
    npm install passport-local --save

    默认本地验证是通过用户名和密码来进行验证的,需要对 Strategies 进行配置:

    assport.use(new LocalStrategy(
          function(username, password, done) {
              //操作
        })
    })

    我这个项目是使用邮箱和密码来进行验证的,方法有所不同:

    passport.use(new LocalStrategy({usernameField: 'email'}, (email, password, done) => {
        //操作
      }))
    LocalStrategy 的第一个参数对象为一个字符串,用来说明是用户验证是的是一个 email;
              第二个参数是从 passport 方法中获取并传回来的表单中的值,也就是要验证的字段。
              第二个参数中的 done 为验证回调,在passport.use()里面,done()有三种用法
    • 当发生系统级异常时,返回done(err),这里是数据库查询出错
    • 当验证不通过时,返回done(null, false, message),这里的message是可选的,可通过express-flash调用。express-flash 为验证提示信息:登录成功 / 登录失败
    • 当验证通过时,返回done(null, user)

    login 在这里的登录方法,登录过程中的密码验证在 passport.js 中一起操作:

    const express = require('express');
    const router = express.Router();
    const mongoose = require('mongoose');
    const bodyParser = require('body-parser');
    const bcrypt = require('bcrypt');
    const passport = require('passport');

    const urlencodedParser = bodyParser.urlencoded({ extended: false });


    router.post("/login",urlencodedParser,(req,res,next) => { const loginUser = { email:req.body.email, password:req.body.password }; console.log(loginUser); passport.authenticate('local', { successRedirect:'/', failureRedirect: '/admin/login', failureFlash: true })(req, res, next); })

    这里的passport.authenticate(‘local’)就是中间件,若通过就进入后面的回调函数,并且给res加上res.user,若不通过则默认返回401错误。

    authenticate()方法有3个参数,第一是name,即验证策略的名称,第二个是options,包括下列属性:

    • session:Boolean。设置是否需要session,默认为true
    • successRedirect:String。设置当验证成功时的跳转链接
    • failureRedirect:String。设置当验证失败时的跳转链接
    • failureFlash:Boolean or String。设置为Boolean时,express-flash将调用use()里设置的message。设置为String时将直接调用这里的信息。
    • successFlash:Boolean or String。使用方法同上。

    第三个参数是callback。

    
    
    const LocalStrategy = require('passport-local').Strategy;
    const bcrypt = require('bcrypt');

    // Load user model
    const UserSchema = require('../models/UserSchema');
     
    module.exports = function(passport){
      passport.use(new LocalStrategy({usernameField: 'email'}, (email, password, done) => {
      console.log('获取的字段:' + email+'/'+ password)
      // Match user
      UserSchema.findOne({
        email:email
      }).then(users => {
        console.log('user存在吗?' + users)
        if(!users){
          return done(null, false, {message: 'No User Found'});
        }

      // Match password
      bcrypt.compare(password, users.password, (err, isMatch) => {
        console.log('password ' + password);
        console.log('user.password' + users.password);
        if(err) throw err;
        if(isMatch){
          console.log('isMatch ' + isMatch)
          return done(null, users);
        } else {
          return done(null, false, {message: 'Password Incorrect'});
        }
      })
     })
    }));


    /*
    *下面两个方法是用来对数据进行序列化的 * serializeUser 将users.id序列化到session中 * deserializeUser 若id存在则从数据库中查询users并存储与req.users中 * * 此处的 users 为表名 */ passport.serializeUser(function(users, done) { done(null, users.id); }); passport.deserializeUser(function(id, done) { UserSchema.findById(id, function(err, users) { done(err, users); }); }); }

    登录验证方法还没有完全搞懂,如果哪里有不对的地方,欢迎指正。

  • 相关阅读:
    python入门_老男孩_文件操作
    python入门_老男孩_列表和字典循环删除的正确方法
    python入门_老男孩_集合_元祖
    linux入门_韩顺平_复习版_文件目录类
    python-re模块
    sorted()函数
    偏函数+高阶函数
    生成器
    闭包
    匿名函数
  • 原文地址:https://www.cnblogs.com/baiyygynui/p/10298776.html
Copyright © 2011-2022 走看看