首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >React/下一步,使用passport.js的Express应用程序无需注册即可登录帐户?

React/下一步,使用passport.js的Express应用程序无需注册即可登录帐户?
EN

Stack Overflow用户
提问于 2019-12-30 00:56:00
回答 1查看 69关注 0票数 1

我使用MongoDB Atlas作为我的数据库来存储我的应用程序的用户。

然而,我只是注意到,虽然我的应用程序阻止已注册的用户再次注册,但我不明白为什么它一开始就要验证不在MongoDB中的用户??

我使用passport.js进行身份验证,特别是local strategy

这是我的模型:

代码语言:javascript
复制
/* eslint-disable no-var */
var mongoose = require('mongoose')
var emailValidator = require('email-validator')
var bcrypt = require('bcrypt') // hashing function dedicated for passwords

const SALT_ROUNDS = 12

var UserSchema = new mongoose.Schema({
    username: {
        type: String,
        required: true,
        lowercase: true,
        index: { unique: true },
        validate: {
            validator: emailValidator.validate,
            message: props => `${props.value} is not a valid email address`
        }
    },
    password: {
        type: String,
        required: true,
        trim: true,
        index: { unique: true },
        minlength: 8
    }
}, {
    timestamps: true
})

UserSchema.pre('save', async function preSave(next) {
    var user = this
    var hash
    if (!user.isModified('password')) return next()
    try {
        hash = await bcrypt.hash(user.password, SALT_ROUNDS)
        user.password = hash
        return next()
    } catch (err) {
        return next(err)
    }
})

UserSchema.methods.comparePassword = async function comparePassword(candidate) {
    return bcrypt.compare(candidate, this.password)
};

module.exports = mongoose.model('User', UserSchema)

这是我身份验证文件,

代码语言:javascript
复制
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var UserModel = require('../models/UserModel');


passport.use(new LocalStrategy({ usernameField: 'username', passwordField: 'password'  }, async(username, password, done) => {
    try {
        var user = await UserModel.findOne({ username: username }).exec();
        if (!user) {
            return done(null, false, { message: 'Invalid username or password' })
        }
        var passwordOk = await user.comparePassword(password);
        if (!passwordOk) {
            return done(null, false, { message: 'Invalid username or password' })
        }
        return done(null, user)
    } catch (err) {
        return done(err)
    }
}))

passport.serializeUser((user, done) => {
    return done(null, user._id)
})

passport.deserializeUser(async(id, done) => {
    try {
        var user = await UserModel.findById(id).exec();
        return done(null, user);
    } catch (err) {
        return done(err)
    }
})

module.exports = {
    initialize: passport.initialize(),
    session: passport.session(),
    setUser: (req, res, next) => {
        res.locals.user = req.user;
        next();
    }
}

这是我在服务器上的中心应用程序文件。

代码语言:javascript
复制
const express = require('express');

require('dotenv').config()

const nextJS = require('next');
var cookieParser = require('cookie-parser')
var session = require('express-session')
var MongoStore = require('connect-mongo')(session)
var bodyParser = require('body-parser')
var auth = require('./lib/auth');
var cors = require('cors')
var morgan = require('morgan')
var HttpStatus = require('http-status-codes')
var PORT = process.env.PORT || 8016

const { isBlockedPage, isInternalUrl } = require('next-server/dist/server/utils');

function NODE_ENVSetter(ENV) {
    var environment,
        environments = {
            'production': () => {
                environment = process.env.PRODUCTION_DB_DSN;
                return environment;
            },
            'test': () => {
                environment = process.env.TEST_DB_DSN;
                return environment;
            },
            'default': () => {
                environment = process.env.DEVELOPMENT_DB_DSN;
                console.log("environment ", environment);
                return environment;
            },
        };
    (environments[ENV] || environments['default'])();

    return environment
}

var db = NODE_ENVSetter('development')
var mongoose = require('mongoose')

function errorHandler(err, req, res, next) {
    // Set locals, only providing error in development
    res.locals.message = err.message
    res.locals.error = req.app.get('env') === 'development' ? err : {}

    // Log error
    console.error(err.stack)

    // Render the error page
    res.status(err.status || 500)

    // Default error message by HTTP code
    res.render('error', {
        title: HttpStatus.getStatusText(err.status),
        message: HttpStatus.getStatusText(err.status)
    })
}

async function start() {
    const dev = process.env.NODE_ENV !== 'production';
    const app = nextJS({ dev });
    const server = express();
    await app.prepare()
        .then(() => {
            mongoose.connect(db, { useNewUrlParser: true })
            mongoose.Promise = global.Promise

            mongoose.connection
                .on('connected', () => {
                    console.log(`Mongoose connection open on ${db}`)
                })
                .on('error', err => {
                    console.log(`Connection error: ${err.message}`)
                });
        })
        .catch(err => {
            console.error(err)
        })

    server.use('/uploads', express.static(__dirname + '/uploads'))
    server.use(bodyParser.json())
    server.use(bodyParser.urlencoded({ extended: true }))
    server.use(morgan('dev'))

    server.use(cookieParser())

    server.use(session({
        secret: 'very secret 12345',
        resave: true,
        saveUninitialized: false,
        store: new MongoStore({ mongooseConnection: mongoose.connection })
    }));

    server.use(auth.initialize);
    server.use(auth.session);
    server.use(auth.setUser);

    server.use(cors())
    server.use('/users', require('./users'))
    server.use('/images', require('./images'))

    // Redirect all requests to main entrypoint pages/index.js
    server.get('/*', async(req, res, next) => {
        try {
            // @NOTE code duplication from here
            // https://github.com/zeit/next.js/blob/cc6fe5fdf92c9c618a739128fbd5192a6d397afa/packages/next-server/server/next-server.ts#L405
            const pathName = req.originalUrl;
            if (isInternalUrl(req.url)) {
                return app.handleRequest(req, res, req.originalUrl)
            }

            if (isBlockedPage(pathName)) {
                return app.render404(req, res, req.originalUrl)
            }

            // Provide react-router static router with a context object
            // https://reacttraining.com/react-router/web/guides/server-rendering
            req.locals = {};
            req.locals.context = {};
            const html = await app.renderToHTML(req, res, '/', {});

            // Handle client redirects
            const context = req.locals.context;
            if (context.url) {
                return res.redirect(context.url)
            }

            // Handle client response statuses
            if (context.status) {
                return res.status(context.status).send();
            }

            // Request was ended by the user
            if (html === null) {
                return;
            }

            app.sendHTML(req, res, html);
        } catch (e) {
            next(e);
        }
    });

    server.use(function(req, res, next) {
        res.status(404).send('404 - Not Found!');
    });

    // eslint-disable-next-line func-names
    server.use(errorHandler, function(error, req, res, next) {
        res.json({ message: error.message })
    })

    server.listen(PORT, err => {
        if (err) throw err;
        console.log(`> Ready on http://localhost:${PORT}`)
    });
}

start();

这是我的客户端组件:

代码语言:javascript
复制
import React, { Component } from 'react'
import { Loader, Dimmer, Transition, Button, Form, Grid, Header, Message, Segment } from 'semantic-ui-react'
import Link from 'next/link';
import { login } from 'next-authentication'

import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { logInUser } from '../../store/reducers/users/index'

class LoginForm extends Component {
 constructor(props) {
  super(props)

  this.state = {
   fadeUp: 'fade up',
   duration: 500,
   username: '',
   password: '',
   usernameError: false,
   passwordError: false,
   formSuccess: false,
   formError: false,
   isLoading: true,
  }

  this.handleChange = this.handleChange.bind(this)
  this.handleBlur = this.handleBlur.bind(this)
  this.handleSubmit = this.handleSubmit.bind(this)

 }

 componentDidMount() {
  this.setState({ isLoading: false })
 }

 handleChange(event) {
  var { name, value } = event.target;
  this.setState({
   [name]: value
  })
 }

 handleBlur() {
  var { username } = this.state;
  var error = false;

  var mailFormat = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;

  if ((!username.match(mailFormat)) || (!username)) {
   error = true;
   this.setState({ usernameError: true });
  } else {
   this.setState({ usernameError: false, });
  }
 }

 handleSubmit(event) {
  event.preventDefault();

  this.setState({
   isLoading: true
  })

  var error = false;
  var { username, password, isLoading } = this.state;
  var { history } = this.props

  var mailFormat = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/

  if (!username.match(mailFormat)) {
   this.setState({ usernameError: true });
   error = true;
  } else {
   this.setState({ usernameError: false });
  }

  if (password.length < 8) {
   this.setState({ passwordError: true });
   error = true;
  } else {
   this.setState({ passwordError: false })
  }

  if (error) {
   this.setState({ formSuccess: false });
   return;
  }

  return window.fetch('http://localhost:8016/users/login', {
   method: 'POST',
   headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
   body: JSON.stringify({ username: username, password: password })
  })
   .then((response) => {
    console.log('response', response)
    if (response.ok) {

     const { token } = response.clone();

     const loginOptions = {
      token,
      cookieOptions: { expires: 1 },
      callback: () => history.push('/profile')
     }

     setTimeout(() => {
      this.props.logInUser()
      login(loginOptions);
     }, 5000)

     this.setState({
      username: '' , password: '', formError: false, formSuccess: true, isLoading: false
     })
     return response.json();
    } else if (!response.ok) {
     if (response.status === 404) {
      console.log("response.status ", response.status);
       console.log("isLoading 2", isLoading) 
      this.setState({
       formError: true, formSuccess: false, isLoading: false
      });
      return;
     }
    }

    return response;
   })
   .catch(err => console.dir(err))

 }

 render() {
  var { username, password, usernameError, passwordError, formSuccess, formError, duration, isLoading } = this.state;
  console.log("LoginForm this.props ", this.props);

  var { isLoggedIn } = this.props;

 console.log("isLoggedIn ", isLoggedIn);
  (formSuccess === true) ? isLoggedIn = true : isLoggedIn = false;

  return (<div className='login-form'> {

  }<style>{`body > div, body > div > div, body > div > div > div.login-form { height: 100%;}`} </style>

   <Grid textAlign='center'
    style={{ height: '100%' }}
    verticalAlign='middle' >
    <Grid.Column style={{ maxWidth: 450 }}>
     <Header as='h2'
      color='teal'
      textAlign='center'>
      Log-in to your account
     </Header>

     <Form size='large'
      onSubmit={this.handleSubmit}
      error={formError}>


      <Segment stacked>
       <Form.Input fluid icon='user'
        iconPosition='left'
        placeholder='E-mail address, e.g. joe@schmoe.com'
        name='username'
        value={username}
        onBlur={this.handleBlur}
        onChange={this.handleChange}
        error={usernameError}
       />

       <Transition visible={usernameError}
        animation='scale'
        duration={duration}>
        <Message error content='username_Email is in incorrect format e.g. joe@schmoe.com' />
       </Transition>

       <Form.Input fluid icon='lock'
        iconPosition='left'
        placeholder='Password'
        name='password'
        value={password}
        onBlur={this.handleBlur}
        onChange={this.handleChange}
        error={passwordError}
       />

       <Transition visible={passwordError}
        animation='scale'
        duration={duration}>
        <Message error content='Password is incorrect, please try again.' />
       </Transition>

       <Button color='teal'
        fluid size='large'
        disabled={!username || !password}>
        Log-in
       </Button>

       <Transition visible={formError}
        unmountOnHide={true}
        animation='scale'
        duration={duration}>

        {isLoading ?
         <Dimmer active inverted>
          <Loader />
         </Dimmer>
         :
         <Message
          error
          centered="true" header='This email does not exist...'
          content='Please re-enter another email address, or  click the link below to register.' />
        }
       </Transition>



       <Transition visible={formSuccess}
        unmountOnHide={true}
        animation='scale'
        duration={duration}>
        {isLoading ?
         <Dimmer active inverted>
          <Loader />
         </Dimmer>
         :
         <Message
          success
          header='Your have successfully logged in.'
          content='Welcome to Hillfinder!' />
        }
       </Transition>

      </Segment>
     </Form>

     {formError ?
      <Transition visible={formError}
       animation='scale'
       duration={1000}>
       <Message>
        <Link href="/register">
         <a>Register</a>
        </Link> </Message>
      </Transition>
      : null
     }
    </Grid.Column> </Grid> </div>
  )
 }
}

function mapStateToProps(state) {
 console.log("state ", state);
 const { users } = state
 console.log("users ", users);
 const { isLoggedIn } = users
  console.log("isLoggedIn ", isLoggedIn);
 return { isLoggedIn }
}
const mapDispatchToProps = dispatch =>
 bindActionCreators({ logInUser }, dispatch)

export default connect(
 mapStateToProps,
 mapDispatchToProps
)(LoginForm)
EN

回答 1

Stack Overflow用户

发布于 2019-12-30 07:15:45

我还没有测试整个东西,但bug可能就在这里:

代码语言:javascript
复制
var user = await UserModel.findOne({ username: username }).exec();

使用async/await,您不会以这种方式执行承诺,您只需等待它:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

另一点是将bcrypt与passportJS相结合,我认为这不是正确的方法,请参阅文档:

http://www.passportjs.org/docs/username-password/#configuration

PassportJS有它自己的内置散列,所以它可能会混淆!:)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59521420

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档