javascript - 登录路由返回未通过身份验证的消息

标签 javascript postgresql express sequelize.js postman

我正在使用 Postman 测试我的路线,包括我遇到问题的注销路线。它不断发送我在 Controller 文件中创建的错误消息,说“未通过身份验证”,而不是向我发送消息“注销”。我不知道问题出在我的服务器文件、我的路由还是我使用 Sequelize 创建的模型中。关于如何纠正这个问题的任何想法?这是我的文件:
用户 Controller .js

const express = require('express');
const bcrypt = require('bcryptjs');

const router = express.Router();

// grab the User model from the models folder, the sequelize
// index.js file takes care of the exporting for us and the
// syntax below is called destructuring, its an es6 feature
const { User } = require('../models');

/* Register Route
========================================================= */
router.post('/register', async (req, res) => {

  // hash the password provided by the user with bcrypt so that
  // we are never storing plain text passwords. This is crucial
  // for keeping your db clean of sensitive data
  const hash = bcrypt.hashSync(req.body.password, 10);

  try {
    // create a new user with the password hash from bcrypt
    let user = await User.create(
      Object.assign(req.body, { password: hash })
    );

    // data will be an object with the user and it's authToken
    let data = await user.authorize();

    // send back the new user and auth token to the
    // client { user, authToken }
    return res.json(data);

  } catch(err) {
    return res.status(400).send(err);
  }

});

/* Login Route
========================================================= */
router.post('/login', async (req, res) => {
  const { username, password } = req.body;

  // if the username / password is missing, we use status code 400
  // indicating a bad request was made and send back a message
  if (!username || !password) {
    return res.status(400).send(
      'Request missing username or password param'
    );
  } 

  try {
    let user = await User.authenticate(username, password)

    user = await User.authorize(user);

    return res.json(user);

  } catch (err) {
    return res.status(400).send('invalid username or password' + err);
  }

});

/* Logout Route
========================================================= */
router.delete('/logout', async (req, res) => {

  // because the logout request needs to be send with
  // authorization we should have access to the user
  // on the req object, so we will try to find it and
  // call the model method logout
  const { user, cookies: { auth_token: authToken } } = req

  // we only want to attempt a logout if the user is
  // present in the req object, meaning it already
  // passed the authentication middleware. There is no reason
  // the authToken should be missing at this point, check anyway
  if (user && authToken) {
    await req.user.logout(authToken);
    return res.status(204).send("Logged out")
  }

  // if the user missing, the user is not logged in, hence we
  // use status code 400 indicating a bad request was made
  // and send back a message
  return res.status(400).send(
    { errors: [{ message: 'not authenticated' }] }
  );
});

/* Me Route - get the currently logged in user
========================================================= */
router.get('/me', (req, res) => {
  if (req.user) {
    return res.send(req.user);
  }
  res.status(404).send(
    { errors: [{ message: 'missing auth token' }] }
  );
});

// export the router so we can pass the routes to our server
module.exports = {"AccountRoutes" : router};
custom-auth-middleware.js
const { User, AuthToken } = require('../models');

module.exports = async function(req, res, next) {

  // look for an authorization header or auth_token in the cookies
  const token =
    req.cookies.auth_token || req.headers.authorization;

  // if a token is found we will try to find it's associated user
  // If there is one, we attach it to the req object so any
  // following middleware or routing logic will have access to
  // the authenticated user.
  if (token) {
    
    // look for an auth token that matches the cookie or header
    const authToken = await AuthToken.find(
      { where: { token }, include: User }
    );

    // if there is an auth token found, we attach it's associated
    // user to the req object so we can use it in our routes
    if (authToken) {
      req.user = authToken.User;
    }
  }
  next();
}
authtoken.js 模型
const {
  Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
  class AuthToken extends Model {
    /**
     * Helper method for defining associations.
     * This method is not a part of Sequelize lifecycle.
     * The `models/index` file will call this method automatically.
     */
    static associate(models) {
      AuthToken.belongsTo(models.User)
    }
  };
  AuthToken.init({
    token: {
      type: DataTypes.STRING,
      allowNull: false,
    },
  }, {
    sequelize,
    modelName: 'AuthToken',
  });

  // generates a random 15 character token and
  // associates it with a user
  AuthToken.generate = async function(UserId) {
    if (!UserId) {
      throw new Error('AuthToken requires a user ID')
    }

    let token = '';

    const possibleCharacters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
      'abcdefghijklmnopqrstuvwxyz0123456789';

    for (var i = 0; i < 15; i++) {
      token += possibleCharacters.charAt(
        Math.floor(Math.random() * possibleCharacters.length)
      );
    }

    return AuthToken.create({ token, UserId })
  }

  return AuthToken;
};
index.js 模型
// require the node packages
var fs        = require('fs');
var path      = require('path');
var Sequelize = require('sequelize');
// set a reference to this file's name so we can exclude it later
var basename  = path.basename(__filename);
// create a variable called env which will pull from the process.env
// or default to 'development' if nothing is specified
var env       = process.env.NODE_ENV || 'development';
// require the config file that was generated by sequelize and use the
// env variable we just created to pull in the correct db creds
var config    = require(__dirname + '/../config/config.json')[env];
// initalize a db object
var db        = {};
// we can set an optional property on our config objects call
// 'use_env_variable' if wanted to set our db credentials on our
// process.env. This is primarily used when deploying to a remote
// server (in production)
if (config.use_env_variable) {
  var sequelize = new Sequelize(
    process.env[config.use_env_variable], config
  );
} else {
  
  // otherwise we use the config object to initialize our sequelize
  // instance
  var sequelize = new Sequelize(
    config.database, config.username, config.password, config
  );
}
// This gathers up all the model files we have yet to create, and
// puts them onto our db object, so we can use them in the rest
// of our application
fs
  .readdirSync(__dirname)
  .filter(file => {
    return (file.indexOf('.') !== 0)
    && (file !== basename)
    && (file.slice(-3) === '.js');
  })
  .forEach(file => {
    var model = sequelize['import'](path.join(__dirname, file));
    db[model.name] = model;
  });
Object.keys(db).forEach(modelName => {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});
// export the main sequelize package with an uppercase 'S' and 
// our own sequelize instance with a lowercase 's'
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
user.js 模型
var bcrypt = require("bcryptjs");

const {
  Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
  class User extends Model {
    /**
     * Helper method for defining associations.
     * This method is not a part of Sequelize lifecycle.
     * The `models/index` file will call this method automatically.
     */
    static associate(models) {
      User.hasMany(models.AuthToken);
    }
  };

  // This is a class method, it is not called on an individual
  // user object, but rather the class as a whole.
  // e.g. User.authenticate('user1', 'password1234')
  User.authenticate = async function(username, password) {

    const user = await User.findOne({ where: { username } });

    // bcrypt is a one-way hashing algorithm that allows us to 
    // store strings on the database rather than the raw
    // passwords. Check out the docs for more detail
    if (bcrypt.compareSync(password, user.password)) {
      return user.id;
    }

    throw new Error('invalid password');
  }

  // in order to define an instance method, we have to access
  // the User model prototype. This can be found in the
  // sequelize documentation
  User.authorize = async function (userId) {
    const { AuthToken } = sequelize.models;
    const user = this

    // create a new auth token associated to 'this' user
    // by calling the AuthToken class method we created earlier
    // and passing it the user id
    const authToken = await AuthToken.generate(userId);

    // addAuthToken is a generated method provided by
    // sequelize which is made for any 'hasMany' relationships

    return { user, authToken }
  };


  User.logout = async function (token) {

    // destroy the auth token record that matches the passed token
    sequelize.models.AuthToken.destroy({ where: { token } });
  };
  
  User.init({
    email: {
      type: DataTypes.STRING, 
      unique: true,
      allowNull: false
    },
    username: {
      type: DataTypes.STRING,
      unique: true,
      allowNull: false
    },
    password: {
      type: DataTypes.STRING,
      allowNull: false
    },
  }, {
    sequelize,
    modelName: 'User',
  });
  return User;
};
index.js “服务器文件”
require('dotenv').config()
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const cookieParser = require('cookie-parser');
const customAuthMiddleware = require('./middleware/custom-auth-middleware');
const session = require('express-session');

// Imports the account route created in user-controller
var AccountRoutes = require('./controllers/user-controller');

// Requiring our models for syncing
const db = require('./models/index');

// directory references
const clientDir = path.join(__dirname, '../client');

// set up the Express App
const app = express();
const PORT = process.env.PORT || 8080;

// Express middleware that allows POSTing data
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

// configure express
app.set('view engine','js');

// use the cookie-parser to help with auth token,
// it must come before the customAuthMiddleware
app.use(cookieParser());
app.use(customAuthMiddleware);

// serve up the public folder so we can request static
// assets from the client
app.use(express.static(`${clientDir}/public`));

// initialize express-session to allow us track the logged-in user across sessions.
app.use(session({
  key: 'user_sid',
  secret: 'somerandonstuffs',
  resave: false,
  saveUninitialized: false,
  cookie: {
      expires: 600000
  }
}));

// This middleware will check if user's cookie is still saved in browser and user is not set, then automatically log the user out.
// This usually happens when you stop your express server after login, your cookie still remains saved in the browser.
app.use((req, res, next) => {
  if (req.cookies.user_sid && !req.session.user) {
      res.clearCookie('user_sid');        
  }
  next();
});

// Include our routes created in user-controller.js
app.use('/',AccountRoutes.AccountRoutes);

// sync our sequelize models and then start server
// force: true will wipe our database on each server restart
// this is ideal while we change the models around
db.sequelize.sync({ force: false }).then(() => {
  
  
    // inside our db sync callback, we start the server
    // this is our way of making sure the server is not listening 
    // to requests if we have not made a db connection
    app.listen(PORT, () => {
      console.log(`App listening on PORT ${PORT}`);
    });
  });

最佳答案

当您请求注销 Postman 时,您是否将 token 与请求一起发送?您获得 400: 'not authenticated' 的原因是您的用户,而 authToken 未定义。这两个未定义的原因在于您的中间件 custom-auth-middleware.js 。当您提出请求时,它没有 token 。或者您的选择查询在数据库中没有找到任何内容,因此 authTokenundefined => req.user` 也未定义。
我认为您不需要将 customAuthMiddleware 作为全局中间件。该中间件仅用于登录用户 => 所以你应该把它放在需要检查用户是否登录的路由之前。在您的示例中,仅注销需要中间件,登录和注册可以是公共(public)路由。您应该删除 index.js 中的这一行代码 app.use(customAuthMiddleware); 并将其添加到您的注销路由中:

router.delete('/logout',customAuthMiddleware, async (req, res) => {
我会像这样重新制作您的 customAuthMiddleware 中间件:
module.exports = async function(req, res, next) {

  // look for an authorization header or auth_token in the cookies
  const token =
    req.cookies.auth_token || req.headers.authorization;

  // if a token is found we will try to find it's associated user
  // If there is one, we attach it to the req object so any
  // following middleware or routing logic will have access to
  // the authenticated user.
  if (!token)
    return res.status(400).send(
      { errors: [{ message: 'No token. Not authenticated!!' }] }
    
  // look for an auth token that matches the cookie or header
  const authToken = await AuthToken.find(
    { where: { token }, include: User }
  );

  // if there is an auth token found, we attach it's associated
  // user to the req object so we can use it in our routes
  if (authToken) {
    req.user = authToken.User;
  }
  
  next();
}
由于您只将这个中间件放在需要登录的路由中,因此公共(public)路由不会受到影响。

关于javascript - 登录路由返回未通过身份验证的消息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64960654/

相关文章:

javascript - 即使在 href 重定向后也能 onclick 吗?

javascript - 返回最大数数组

javascript - 如何在带有 Javascript 源的 Firefox 扩展(XUL 文件)中使用 SQLite?

postgresql - PostgreSQL 中的 PL/pgSQL 查询返回新的空表的结果

postgresql - 每个表的索引数

node.js - 如何将新的 View /路线添加到 Express 中?

node.js - 如何从express上传的图片中获取图片分辨率数据?

javascript - Angular JS - 使服务可以从 Controller 和 View 全局访问

ruby-on-rails - 保存失败后 Rails 不回滚事务()

node.js - 无法让样式表与 node.js 的 ejs 一起使用