nodeJS---express4+passport实现用户注册登录验证
网上有很多关于passport模块使用方法的介绍,不过基本上都是基于express3的,本文介绍在express4框架中使用passport模块。
前言
passport是一个功能单一,但非常强大的一个模块,支持本地账号验证和第三方账号登录验证,本文将介绍使用passport实现用户注册检测,用户登录验证。
passport是使用”策略“来验证请求,策略是passport中最重要的概念。passport模块本身不能做认证,所有的认证方法都以策略模式封装为插件,需要某种认证时将其添加到package.json即可。
策略模式是一种设计模式,它将算法和对象分离开来,通过加载不同的算法来实现不同的行为,适用于相关类的成员相同但行为不同的场景,比如在passport中,认证所需的字段都是用户名、邮箱、密码等,但认证方法是不同的。
安装
1)先创建工程
fan@dev:~/nodejs$ express -e node_passport
fan@dev:~/nodejs$ cd node_passport/
fan@dev:~/nodejs/node_passport$npm install //进入工程文件,安装依赖
2)安装passport
fan@dev:~/nodejs/node_passport$ npm install passport --save
3)安装插件
你最少需要安装一个passport策略来使用它,一般而言本地验证策略passport-local是必装的。
fan@dev:~/nodejs/node_passport$ npm install passport-local --save
基本使用方法
local本地验证
1)配置Strategies
本地验证默认是通过用户名和密码来验证的,代码如下:
assport.use(new LocalStrategy(
function(username, password, done) {
//操作
})
})
也可以通过配置,采用邮箱和密码来验证,本文要介绍的就是通过邮箱和密码验证。
代码如下:
passport.use('local.signup',new localStategy({
usernameField:'email',
passwordField:'password',
passReqToCallback:true //此处为true,下面函数的参数才能有req
},function(req,email,password,done){
//....省略代码
})
})
注意:
- passport.use的第一个参数是一个字符串,是用来标识验证方法的,因为一个工程中,可能会有多此验证,每次验证的逻辑会不一样,下面会一一讲解。
- 实例化的第一个参数是一个对象,在对象里面添加你要验证的字段,其中passReqToCallback项,默认为false,设置为true时可以将整个req传递给回调函数,这样在回调里就可以验证req中带的所有条件。
在工程中实现passport的配置:
新建一个config文件下,进入该文件夹目录下,新建passport.js文件:
fan@dev:~/nodejs/node_passport$ mkdir config
fan@dev:~/nodejs/node_passport$ cd config
fan@dev:~/nodejs/node_passport/config$ vim passport.js
passport.js代码如下:
var passport = require('passport');
var User = require("../models/user.js");
var localStategy = require('passport-local').Strategy;
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
passport.use('local.signup',new localStategy({
usernameField:'email',
passwordField:'password',
passReqToCallback:true //此处为true,下面函数的参数才能有req
},function(req,email,password,done){
req.checkBody('email','您输入的email无效').notEmpty().isEmail();
req.checkBody('password',"您输入了无效密码").notEmpty().isLength({min:});
var errors = req.validationErrors();
if(errors){
var messages = [];
errors.forEach(function(error){
messages.push(error.msg);
});
return done(null,false,req.flash('error',messages));
}
User.findOne({'email':email},function(err,user){
if(err){
return done(err);
}
if(user){
return done(null,false,{message:"此邮件已经被注册"});
}
var newUser = new User();
newUser.email = email;
newUser.password = newUser.encryptPassword(password);
newUser.save(function(err,result){
if(err){
return done(err);
}
return done(null,newUser);
});
});
}));
passport.use('local.login',new localStategy({
usernameField:'email',
passwordField:'password',
passReqToCallback:true //此处为true,下面函数的参数才能有req
},function(req,email,password,done){
req.checkBody('email','您输入的email无效').notEmpty();
req.checkBody('password',"您输入了无效密码").notEmpty();
var errors = req.validationErrors();
if(errors){
var messages = [];
errors.forEach(function(error){
messages.push(error.msg);
});
return done(null,false,req.flash('error',messages));
}
User.findOne({'email':email},function(err,user){
if(err){
return done(err);
}
if(!user){
return done(null,false,{message:"用户名错误!"});
}
if(!user.validPassword(password)){
return done(null,false,{message:"密码错误!"});
}
return done(null,user);
});
}));
上面代码配置了用户注册时验证和用户登录时验证的策略,上面代码使用到:
var User = require("../models/user.js"); //用户集合模型
newUser.encryptPassword()
user.validPassword()
代码如下:
var mongoose = require('mongoose');
var bcrypt = require('bcrypt-nodejs');
var Schema = mongoose.Schema;
var userchema = new Schema({
email:{type:String,required:true},
password:{type:String,required:true}
});
userchema.methods.encryptPassword = function(password){
return bcrypt.hashSync(password,bcrypt.genSaltSync(),null);
};
userchema.methods.validPassword = function(password){
return bcrypt.compareSync(password,this.password);
};
module.exports = mongoose.model('User',userchema);
req.checkBody(); //通过var validator = require('express-validator'); 引入
使用方法:
在app.js文件中添加:
var validator = require('express-validator'); app.use(validator());
2)验证回调(会看config/passport.js代码)
在passport.use()里面,done()有三种用法:
- 当发生系统级异常时,返回done(err),这里是数据库查询出错,一般用next(err),但这里用done(err),两者的效果相同,都是返回error信息;
- 当验证不通过时,返回done(null, false, message),这里的message是可选的,可通过express-flash调用;
- 当验证通过时,返回done(null, user)。
3)session序列化与反序列化(会看config/passport的代码)
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
这里第一段代码是将环境中的user.id序列化到session中,即sessionID,同时它将作为凭证存储在用户cookie中。
第二段代码是从session反序列化,参数为用户提交的sessionID,若存在则从数据库中查询user并存储与req.user中。
4)Authenticate验证
routes/user.js中部分代码
router.post('/signup',
passport.authenticate('local.signup',{
successRedirect:'/user/profile',
failureRedirect:'/user/signup',
failureFlash:true
}),function(req,res,next){
});
router.post('/login',
passport.authenticate('local.login',{
successRedirect:'/user/profile',
failureRedirect:'/user/login',
failureFlash:true
}),function(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。注意如果使用了callback,那么验证之后建立session和发出响应都应该由这个callback来做,passport中间件之后不应该再有其他中间件或callback。
5)HTTP request操作
注意上面的代码里有个req.logIn(),它不是http模块原生的方法,也不是express中的方法,而是passport加上的,passport扩展了HTTP request,添加了四种方法。
- logIn(user, options, callback):用login()也可以。作用是为登录用户初始化session。options可设置session为false,即不初始化session,默认为true。
- logOut():别名为logout()。作用是登出用户,删除该用户session。不带参数。
- isAuthenticated():不带参数。作用是测试该用户是否存在于session中(即是否已登录)。若存在返回true。事实上这个比登录验证要用的更多,毕竟session通常会保留一段时间,在此期间判断用户是否已登录用这个方法就行了。
- isUnauthenticated():不带参数。和上面的作用相反。
完整代码:
app.js:
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session');
var exphbs = require('express-handlebars');
var passport = require('passport');
var flash = require('connect-flash');
var validator = require('express-validator');
var MongoStore = require('connect-mongo')(session);
var mongoose = require('mongoose');
mongoose.connect("localhost:27017/shopping");
require('./config/passport.js');
var routes = require('./routes/index');
var userroutes = require('./routes/user');
//var users = require('./routes/users');
var app = express();
app.engine('hbs',exphbs({
defaultLayout:'main',
extname:'.hbs'
}));
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'hbs');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(validator());
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: false,
store:new MongoStore({
mongooseConnection:mongoose.connection
}),
cookie:{maxAge:**} //store保存时间
}));
//对session操作的模块,应在session实例下面
app.use(flash());
app.use(passport.initialize());
app.use(passport.session());
app.use(express.static(path.join(__dirname, 'public')));
app.use(function(req,res,next){
res.locals.login = req.isAuthenticated();
next();
});
app.use('/', routes);
app.use('/user', userroutes);
//app.use('/users', users);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = ;
next(err);
});
// error handlers
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || );
res.render('error', {
message: err.message,
error: err
});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || );
res.render('error', {
message: err.message,
error: {}
});
});
module.exports = app;
说明:express4以后,sessions模块被分离出来了,为了使用req.flash,我们需要安装express-session模块:
var session = require('express-session');
var flash = require('connect-flash');
并配置session:
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: false,
store:new MongoStore({
mongooseConnection:mongoose.connection
}),
cookie:{maxAge:**} //store保存时间
}));
说明:store使用来会话存储的,需要用到connect-mongo模块。
config/passport.js:
var passport = require('passport');
var User = require("../models/user.js");
var localStategy = require('passport-local').Strategy;
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
passport.use('local.signup',new localStategy({
usernameField:'email',
passwordField:'password',
passReqToCallback:true //此处为true,下面函数的参数才能有req
},function(req,email,password,done){
req.checkBody('email','您输入的email无效').notEmpty().isEmail();
req.checkBody('password',"您输入了无效密码").notEmpty().isLength({min:});
var errors = req.validationErrors();
if(errors){
var messages = [];
errors.forEach(function(error){
messages.push(error.msg);
});
return done(null,false,req.flash('error',messages));
}
User.findOne({'email':email},function(err,user){
if(err){
return done(err);
}
if(user){
return done(null,false,{message:"此邮件已经被注册"});
}
var newUser = new User();
newUser.email = email;
newUser.password = newUser.encryptPassword(password);
newUser.save(function(err,result){
if(err){
return done(err);
}
return done(null,newUser);
});
});
}));
passport.use('local.login',new localStategy({
usernameField:'email',
passwordField:'password',
passReqToCallback:true //此处为true,下面函数的参数才能有req
},function(req,email,password,done){
req.checkBody('email','您输入的email无效').notEmpty();
req.checkBody('password',"您输入了无效密码").notEmpty();
var errors = req.validationErrors();
if(errors){
var messages = [];
errors.forEach(function(error){
messages.push(error.msg);
});
return done(null,false,req.flash('error',messages));
}
User.findOne({'email':email},function(err,user){
if(err){
return done(err);
}
if(!user){
return done(null,false,{message:"用户名错误!"});
}
if(!user.validPassword(password)){
return done(null,false,{message:"密码错误!"});
}
return done(null,user);
});
}));
views/signup.ejs:
<%- include header %> <form method="post">
<% if(hasError){ %>
<div class="alert alert-danger">
<% messages.forEach(function(item){ %>
<p> <%= item %> </p>
<% }) %>
</div>
<% } %>
邮箱: <input type="text" name="email" /><br/>
密码: <input type="password" name="password" /><br />
确认密码:<input type="password" name="password-repeat" /><br />
<input type="submit" value="注册" />
</form> <%- include footer %>
测试:
在浏览器中输入localhost:3000/users/signup
密码位数少于4的情况(右图是提交之后的显示):


邮箱错误:


邮箱和密码都错误:


这里只举例用户的注册,用户登录其实也差不多,这里就不举例了。
nodeJS---express4+passport实现用户注册登录验证的更多相关文章
- 用户注册登录验证 多版本集合 + hashlib加密
#!/usr/bin/env python# -*- coding: utf-8 -*-# @Time : 2018/5/6 0006 12:22# @Author : Anthony.Waa# @S ...
- nodejs(一) 简单登录验证 使用mongoose 操作MongoDB
---恢复内容开始--- 开发使用webstorm 9 新建nodejs+express 项目 newfarmer 文章目录 配置Mongoose 创建目录及文件 插入数据,POST提交JSON增加 ...
- express+nodecoffee写passport登录验证实例(一)
项目中要用到passport登录验证,环境如标题样:express框架,coffee模版引擎,node后台 一:建项目 直接用express命令建,虽然默认模版为jade,可以手动换成coffee哦. ...
- nodejs运用passport和passport-local分离本地登录
var express = require('express'); var cookieParser = require('cookie-parser'); var bodyParser = requ ...
- 传智播客JavaWeb day07、day08-自定义标签(传统标签和简单标签)、mvc设计模式、用户注册登录注销
第七天的课程主要是讲了自定义标签.简单介绍了mvc设计模式.然后做了案例 1. 自定义标签 1.1 为什么要有自定义标签 前面所说的EL.JSTL等技术都是为了提高jsp的可读性.可维护性.方便性而取 ...
- 利用nginx向现有网站添加登录验证功能(不添加修改现有网站代码)
在不改变现有网站代码的前提下加入验证功能: 1.假设现有网站后端nodejs,端口3000,nginx配置如下 server { listen 80; server_name localhost; l ...
- 搭建开发框架Express,实现Web网站登录验证
NodeJS学习笔记(一)——搭建开发框架Express,实现Web网站登录验证 JS是脚本语言,脚本语言都需要一个解析器才能运行.对于写在HTML页面里的JS,浏览器充当了解析器的角色.而对于需 ...
- koa使用koa-passport实现路由进入前登录验证
现在的项目需求很简单,当进入一个页面的时候,如果没登录,则跳转到登录页面,如果登录了则直接到对应页面. koa2写的项目,使用koa-passport,koa-session,根据koa-passpo ...
- Mongodb For Mac OSX && 登录验证
题外话:尽管有不少人贴出了 <我不用mongodb的十大理由> 等系列文章,但是 NoSQL 的发展不会因此而止步, mongodb 是 NoSQL 的典型代表,楼主还是抱乐观态度的,有人 ...
随机推荐
- iOS工作小技巧及填坑记录
以下是本人在iOS开发工作中使用的一些小技巧,记录一下. 1.使用XXX.pch文件便捷开发+加速Build 在IOS开发的项目中有一个XX_Prefix.pch XX_Prefix.pch:扩展名. ...
- 传智播客JavaWeb day11--事务的概念、事务的ACID、数据库锁机制、
1. 什么叫做事务? 2.默认情况下每一条sql语句都是一个事务,然后自动提交事务 ps:如果想多条语句占一个事务,则可以手动设置SetAutoCommit为false 3.关键字 start tr ...
- iftop 安装以及相关参数及说明(转载自csdn)
转载自http://blog.csdn.net/cqinter/article/details/6250211 关于 Iftop iftop 是类似于top的实时流量监控工具.主要用来显示本机网络 ...
- 时间改成24小时制 和pc mobile链接自动转化
1 2 <script type ="text/javascript"> function checkserAgent(){ var userAgentInfo=na ...
- 【JS】数字转大写中文
原文参考 逛到一道面试题,数字转大写中文的,搜索学习并记录于此. //自动转换数字金额为大小写中文字符,返回大小写中文字符串,最大处理到999兆 function changeMoneyToChine ...
- response 设置头的类型 (转)
Response.ContentType 详细列表 不同的ContentType 会影响客户端所看到的效果.默认的ContentType为 text/html 也就是网页格式.代码如: <% r ...
- php判断post数据是否存在(or 为空)的方法
最近开发的php项目用到了表单 所以需要响应post请求 而在实际使用中 有些请求只需判断是否存在 百度了不少资料 发现都比较繁杂 然后想起了 count()函数 — 计算数组中的单元数目或对象中的 ...
- OpenGL 4.5 Core Profile管线(GLSL与应用程序接口详解)【未完成】
之前写过一篇博客,OpenGL管线(用经典管线代说着色器内部),说的主要是OpenGL的经典管线.大家都知道,现代OpenGL已经弃用(从OpenGL 3.0开始)经典管线功能(glBegin,变换矩 ...
- Bullet的学习资源(用Doxygen生成API文档)
Bullet 全称 Bullet Physics Library,是著名的开源物理引擎(可用于碰撞检测.刚体模拟.可变形体模拟),这里将bullet的学习资源整理一下,希望能帮助入门者少走弯路. 看下 ...
- JavaScript+HTML,简单的计算器实现
成功进化到程序猿快一年多了, 还没写过计算器, 正好今天比较闲,随手写了个计算器,最简单的实现,核心是eval()方法,把字符串作为JS代码处理,把输入的信息拼接成字符串,点等号执行代码得到结果,出异 ...