在Express+中实现“记住我”功能;护照JS+;Redis应用程序
我正在尝试用ExpressJS和PassportJS构建身份验证系统。对于会话存储,我使用Redis。我想用它来记住我。每次用户登录并选中“记住我”复选框时,应在下次访问站点时自动登录。我已经下载了一个示例应用程序表单Github,并对其进行了更改以供我使用在Express+中实现“记住我”功能;护照JS+;Redis应用程序,express,redis,passport.js,Express,Redis,Passport.js,我正在尝试用ExpressJS和PassportJS构建身份验证系统。对于会话存储,我使用Redis。我想用它来记住我。每次用户登录并选中“记住我”复选框时,应在下次访问站点时自动登录。我已经下载了一个示例应用程序表单Github,并对其进行了更改以供我使用 var express = require('express') , passport = require('passport') , LocalStrategy = require('passport-local').Strate
var express = require('express')
, passport = require('passport')
, LocalStrategy = require('passport-local').Strategy
, mongodb = require('mongodb')
, mongoose = require('mongoose')
, bcrypt = require('bcrypt')
, SALT_WORK_FACTOR = 10
, RedisStore = require('connect-redis')(express);
mongoose.connect('localhost', 'test');
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function callback() {
console.log('Connected to DB');
});
// User Schema
var userSchema = mongoose.Schema({
username: { type: String, required: true, unique: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true},
accessToken: { type: String } // Used for Remember Me
});
// Bcrypt middleware
userSchema.pre('save', function(next) {
var user = this;
if(!user.isModified('password')) return next();
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
if(err) return next(err);
bcrypt.hash(user.password, salt, function(err, hash) {
if(err) return next(err);
user.password = hash;
next();
});
});
});
// Password verification
userSchema.methods.comparePassword = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if(err) return cb(err);
cb(null, isMatch);
});
};
// Remember Me implementation helper method
userSchema.methods.generateRandomToken = function () {
var user = this,
chars = "_!abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",
token = new Date().getTime() + '_';
for ( var x = 0; x < 16; x++ ) {
var i = Math.floor( Math.random() * 62 );
token += chars.charAt( i );
}
return token;
};
// Seed a user
var User = mongoose.model('User', userSchema);
var usr = new User({ username: 'bob', email: 'bob@example.com', password: 'secret' });
usr.save(function(err) {
if(err) {
console.log(err);
} else {
console.log('user: ' + usr.username + " saved.");
}
});
// Passport session setup.
// To support persistent login sessions, Passport needs to be able to
// serialize users into and deserialize users out of the session. Typically,
// this will be as simple as storing the user ID when serializing, and finding
// the user by ID when deserializing.
//
// Both serializer and deserializer edited for Remember Me functionality
passport.serializeUser(function(user, done) {
var createAccessToken = function () {
var token = user.generateRandomToken();
User.findOne( { accessToken: token }, function (err, existingUser) {
if (err) { return done( err ); }
if (existingUser) {
createAccessToken(); // Run the function again - the token has to be unique!
} else {
user.set('accessToken', token);
user.save( function (err) {
if (err) return done(err);
return done(null, user.get('accessToken'));
})
}
});
};
if ( user._id ) {
createAccessToken();
}
});
passport.deserializeUser(function(token, done) {
User.findOne( {accessToken: token } , function (err, user) {
done(err, user);
});
});
// Use the LocalStrategy within Passport.
// Strategies in passport require a `verify` function, which accept
// credentials (in this case, a username and password), and invoke a callback
// with a user object. In the real world, this would query a database;
// however, in this example we are using a baked-in set of users.
passport.use(new LocalStrategy(function(username, password, done) {
User.findOne({ username: username }, function(err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false, { message: 'Unknown user ' + username }); }
user.comparePassword(password, function(err, isMatch) {
if (err) return done(err);
if(isMatch) {
return done(null, user);
} else {
return done(null, false, { message: 'Invalid password' });
}
});
});
}));
var app = express();
// configure Express
app.configure(function() {
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.engine('ejs', require('ejs-locals'));
app.use(express.logger());
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.session({
store: new RedisStore({ host: '127.0.0.1', port: 6379, prefix: 'chs-sess' }),
secret: '4Md97L1bL4r42SPn7076j1FwZvAiqube',
maxAge: new Date(Date.now() + 3600000)
}));
// Remember Me middleware
app.use( function (req, res, next) {
if ( req.method == 'POST' && req.url == '/login' ) {
if ( req.body.rememberme ) {
req.session.cookie.maxAge = 2592000000; // 30*24*60*60*1000 Rememeber 'me' for 30 days
} else {
req.session.cookie.expires = false;
}
}
next();
});
// Initialize Passport! Also use passport.session() middleware, to support
// persistent login sessions (recommended).
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.static(__dirname + '/../../public'));
});
app.get('/', function(req, res){
res.render('index', { user: req.user });
});
app.get('/account', ensureAuthenticated, function(req, res){
res.render('account', { user: req.user });
});
app.get('/login', function(req, res){
res.render('login', { user: req.user, message: req.session.messages });
});
// POST /login
// Use passport.authenticate() as route middleware to authenticate the
// request. If authentication fails, the user will be redirected back to the
// login page. Otherwise, the primary route function function will be called,
// which, in this example, will redirect the user to the home page.
//
// curl -v -d "username=bob&password=secret" http://127.0.0.1:3000/login
//
/***** This version has a problem with flash messages
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login', failureFlash: true }),
function(req, res) {
res.redirect('/');
});
*/
// POST /login
// This is an alternative implementation that uses a custom callback to
// acheive the same functionality.
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err) }
if (!user) {
req.session.messages = [info.message];
return res.redirect('/login')
}
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/');
});
})(req, res, next);
});
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
app.listen(3000, function() {
console.log('Express server listening on port 3000');
});
// Simple route middleware to ensure user is authenticated.
// Use this route middleware on any resource that needs to be protected. If
// the request is authenticated (typically via a persistent login session),
// the request will proceed. Otherwise, the user will be redirected to the
// login page.
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/login')
}
var express=require('express'))
,passport=require('passport')
,LocalStrategy=require('passport-local')。策略
,mongodb=require('mongodb'))
,mongoose=require('mongoose')
,bcrypt=require('bcrypt')
,盐功系数=10
,redistore=require('connect-redis')(express);
connect('localhost','test');
var db=猫鼬连接;
db.on('error',console.error.bind(console,'connectionerror:');
db.once('open',函数回调(){
log('连接到数据库');
});
//用户模式
var userSchema=mongoose.Schema({
用户名:{type:String,必需:true,unique:true},
电子邮件:{type:String,必需:true,unique:true},
密码:{type:String,必需:true},
accessToken:{type:String}//用于记住我
});
//Bcrypt中间件
userSchema.pre('save',函数(下一步){
var user=this;
如果(!user.isModified('password'))返回next();
b晶种genSalt(盐分、功系数、函数(误差、盐分){
if(err)返回next(err);
bcrypt.hash(user.password、salt、函数(err、hash){
if(err)返回next(err);
user.password=hash;
next();
});
});
});
//密码验证
userSchema.methods.comparePassword=函数(candidatePassword,cb){
bcrypt.compare(候选密码、this.password、函数(err、isMatch){
如果(错误)返回cb(错误);
cb(null,isMatch);
});
};
//记住我的实现助手方法
userSchema.methods.generateRandomToken=函数(){
var user=this,
chars=“33;!abcdefghijklmnopqrstuvxyzabcdefghijklmnopqrstuvxyz1234567890”,
令牌=新日期().getTime()+'";
对于(变量x=0;x<16;x++){
var i=Math.floor(Math.random()*62);
代币+=字符(i);
}
返回令牌;
};
//给用户种子
var User=mongoose.model('User',userSchema);
var usr=新用户({用户名:'bob',电子邮件:'bob@example.com,密码:'secret'});
usr.save(函数(错误){
如果(错误){
控制台日志(err);
}否则{
log('user:'+usr.username+“saved.”);
}
});
//Passport会话设置。
//为了支持持久登录会话,Passport需要能够
//将用户序列化到会话中,并从会话中反序列化用户。典型的
//这与序列化时存储用户ID和查找
//反序列化时按ID显示的用户。
//
//序列化程序和反序列化程序都是为“记住我”功能而编辑的
passport.user(函数(user,done){
var createAccessToken=函数(){
var token=user.generateRandomToken();
findOne({accessToken:token},函数(err,existingUser){
if(err){返回完成(err);}
如果(现有用户){
createAccessToken();//再次运行该函数-标记必须是唯一的!
}否则{
user.set('accessToken',token);
user.save(函数(err){
如果(错误)返回完成(错误);
返回done(null,user.get('accessToken');
})
}
});
};
如果(用户id){
createAccessToken();
}
});
passport.deserializeUser(函数(令牌,完成){
findOne({accessToken:token},函数(err,User){
完成(错误,用户);
});
});
//在Passport中使用LocalStrategy。
//passport中的策略需要一个“verify”函数,该函数接受
//凭据(在本例中为用户名和密码),并调用回调
//使用用户对象。在现实世界中,这将查询数据库;
//然而,在本例中,我们使用的是一组烘焙的用户。
passport.use(新的LocalStrategy(函数)(用户名、密码、完成){
findOne({username:username},函数(err,User){
if(err){返回完成(err);}
如果(!user){返回完成(null,false,{消息:'未知用户'+用户名});}
user.comparePassword(密码,函数(err,isMatch){
如果(错误)返回完成(错误);
如果(isMatch){
返回完成(空,用户);
}否则{
返回完成(null,false,{消息:“无效密码”});
}
});
});
}));
var-app=express();
//配置Express
app.configure(函数(){
app.set('views','u dirname+'/views');
应用程序集(“查看引擎”、“ejs”);
应用程序引擎('ejs',要求('ejs-locals');
app.use(express.logger());
use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.methodOverride());
应用程序使用(express.session)({
存储:新的RedisStore({主机:'127.0.0.1',端口:6379,前缀:'chs sess'}),
秘密:“4Md97L1bL4r42SPn7076j1FwZvAiqube”,
maxAge:新日期(Date.now()+3600000)
}));
//记得我吗
应用程序使用(功能(请求、恢复、下一步){
如果(req.method=='POST'&&req.url=='/login'){
如果(要求主体记忆){
req.session.cookie.maxAge=2592000000;//30*24*60*60*1000记住“我”30天
}否则{
req.session.cookie.expires=false;
}
}
next();
});
//初始化Passport!还可以使用Passport.session()中间件来支持
//持久登录会话(推荐)。
app.use(passport.initialize());
app.use(passport.session());
应用程序使用(应用程序路由器);
app.use(express.static(_dirname+'/../../../public');
});
app.get('/',函数(req,res){
res.render('index',{user:req.user});
});
app.get('/account',ensureAuthenticated,函数(req,res){
res.render('account',{user:req.user});
});
app.get('/login',函数(req,res){
res.render('login',{user:req.user,message:req.session.messages});
});
//发布/登录
//使用passport.authenticate()a