【问题标题】:Understanding Passport, Failed Serialization, 401 Error了解 Passport、序列化失败、401 错误
【发布时间】:2018-06-03 13:53:08
【问题描述】:

我今天一直在修改 passport-jwt,试图弄清楚如何在我的应用程序中正确设置它。我在注册用户和保存 jwt 令牌时没有遇到任何问题,但我认为我没有使用护照这样做。现在我正在尝试登录用户,但我总是收到错误Error: Failed to serialize user into session。我在下面的一些文章中看到过:

passport.serializeUser((user, done) => {
  done(null, user.id);
});

passport.deserializeUser((id, done) => {
  User.findById(id, (err, user) => {
      done(err, user);
  });
});

但我似乎找不到任何示例,甚至找不到说明在哪里使用这些的护照文档的配置部分。这可能是我问题的根源,但我不确定。我发现令人困惑的 Passport 方面之一是 JWT 提取器。如果我只想将我的护照转换为 jwt 令牌并将其保存在我的 mongo 数据库中,为什么我需要它们中的任何一个(即使它们是必需的)?自己使用jsonwebtoken 并根据需要对字符串进行哈希/取消哈希处理并使用cookie-parser 创建我自己的会话而不是使用像Passport 这样的东西不是更容易吗?我也尝试在我的休息电话中使用 passport.authencate() 但那些返回 401。您可以在登录调用下方看到失败的尝试被注释掉。

请帮忙,我觉得我是唯一一个发现护照比不那么健壮的东西更复杂的人,但我真的致力于解决它!

import express from 'express';
import path from 'path';
import bodyParser from 'body-parser';
import connection from './database';
import jwt from 'jsonwebtoken';
import cors from 'cors';
import crypto from 'crypto';
import passport from "passport";
import passportJWT from "passport-jwt";
const JwtStrategy = passportJWT.Strategy;
const ExtractJwt = passportJWT.ExtractJwt;
import { User } from './schema';
import { read } from 'fs';
const app = express();
const corsOptions = {
  origin: '*',
  optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
}
app.use(cors(corsOptions));
app.use(passport.initialize());
app.use(passport.session());

const jwtOptions = {
  jwtFromRequest: ExtractJwt.fromBodyField('Password'),
  secretOrKey: 'secretKey',
  jsonWebTokenOptions: { expiresIn: 172800 }
}

passport.use(new JwtStrategy(jwtOptions, (jwt_payload, done) => {
  User.findOne({ password: jwt_payload.sub }, (err, user) => {
    user ? done(null, user) : done(null, false);
  });
}));

passport.serializeUser((user, done) => {
  done(null, user.id);
});

passport.deserializeUser((id, done) => {
  User.findById(id, (err, user) => {
      done(err, user);
  });
});

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

app.use(express.static(path.join(__dirname, 'build')));

app.get('/*', (req, res) => {
  res.sendFile(path.join(__dirname, 'build', 'index.html'));
});

app.post('/test', (req, res) => {
  console.log('Success');
  console.log(req.body);
  res.json(req.body);
});

app.post('/register', async (req, res) => {
  if (req.body.FirstName
    && req.body.LastName
    && req.body.Email
    && req.body.Password
  ) {
    const token = await jwt.sign(
      { Password: req.body.Password },
      jwtOptions.secretOrKey,
      { expiresIn: '48h' }
    );
    const CreatedAt = await Date.now();
    const newUser = await new User({
      FirstName: req.body.FirstName,
      LastName: req.body.LastName,
      Email: req.body.Email,
      Password: token,

    });
    await newUser.save(err => {
      err ? console.log(err) : res.json(newUser);
    });
  }
  throw new('error');
});

// app.post('/login', passport.authenticate('jwt', { session: false }),
//   (req, res) => {
//     console.log(req.user)
//   if (req.body.Email && req.body.Password) {
//     User.findOne({ Email: req.body.Email }, (err, user) => {
//       if (err) return console.log(err);
//       console.log(user);
//       return user;
//     }).then((res) => {
//       jwt.verify(res.Password, jwtOptions.secretOrKey, (err, decoded) => {
//         err ? console.log(err) :
//         req.login(decoded, (err) => {
//           // if (err) { return next(err); };
//           if (err) {
//             console.log(err) 
//           } else {
//             console.log(decoded)
//           }
//         });
//       });
//     }).catch(err => console.log('Failed ', err));
//   } else {
//     console.log(req.body);
//   }
// });

app.post('/login', (req, res) => {
  if (req.body.Email && req.body.Password) {
    User.findOne({ Email: req.body.Email }, (err, user) => {
      if (err) return console.log(err);
      console.log(user);
      return user;
    }).then((res) => {
      // res.password returns the jwt string successfully
      jwt.verify(res.Password, jwtOptions.secretOrKey, (err, decoded) => {
        err ? console.log(err) :
        req.login(decoded, (err) => {
          // if (err) { return next(err); };
          if (err) {
            console.log(err) // always returns the error posted above
          } else {
            console.log(decoded)
          }
        });
      });
    }).catch(err => console.log('Failed ', err));
  } else {
    console.log(req.body);
  }
});

app.listen(9000, () => {
  console.log(`Listening on port 9000`);
});

自定义提取器尝试

    const customExtractor = req => {
  User.findOne({ Email: req.body.Email }, '_id', (err, user) => {
    const token = jwt.sign(
      { id: user },
      'secretKey',
      { expiresIn: '48h' }
    );
    return token;
  });

根据@OrthoHomeDefense 的帖子,我创建了以下内容并成功向客户端发送了一个 jwt 令牌:

app.post("/login", (req, res) => {
  if (req.body.Email && req.body.Password) {
    User.findOne({ Email: req.body.Email }, (err, user) => {
      if(user.Password === req.body.Password) {
        console.log()
        var token = jwt.sign(JSON.stringify(user), jwtOptions.secretOrKey);
        res.json({token: token});
      } else {
        console.log(err);
        res.status(401).json({message:"Passwords did not match."});
      }
    });
  } else {
    res.status(401).json({message:"Please provide an email and a password."});
  }
});

【问题讨论】:

  • 你看过这个吗? jonathanmh.com/…
  • @OrthoHomeDefense 我之前已经通读过它,看起来我被困在 Passport JWT 策略部分,因为我收到了 401。我不明白的部分是提取器本身。为什么我会从任何规定的提取器位置收到 jwt? jwt 不是曾经是提供密码的散列字符串吗?在这种情况下,我不是从数据库中接收 jwt,而不是任何提取器位置吗?我知道我说的肯定是错的,但我似乎不知道怎么做?
  • 你能告诉我你在客户端调用请求时的样子吗?
  • POST localhost:9000/login 401 (Unauthorized) index.js:13 错误:在 XMLHttpRequest 的结算 (settle.js:18) 处的 createError (createError.js:16) 请求失败,状态码为 401。处理负载(xhr.js:77)
  • 您的“承载”标头是什么样的?

标签: javascript express authentication jwt passport.js


【解决方案1】:

首先,@OrthoHomeDefense 解决了我关于设置登录的最初问题,因为我对初始设置需要遵循什么以及在哪里使用护照感到困惑。

以下是我从客户端到服务器的调用。

export const test = token => {
  console.log(token)
  return dispatch => {
    axios.post('http://localhost:9000/test',
    {
      headers: {'authorization': token},
      data: {test: 'test', Token: token}
    }
  ).then((res) => {
      console.log(res.data);
    })
    .catch((error) => { console.log(error); });
  }
}

接下来是我在服务器端做的相关测试。

const customExtractor = req => {
  let token = req.body.headers.authorization;
  console.log(token);
  return token;
}

const jwtOptions = {
  jwtFromRequest: customExtractor,
  secretOrKey: 'secretKey',
}

passport.use(new JwtStrategy(jwtOptions, (jwt_payload, done) => {
  console.log('Success', jwt_payload);
  User.findOne({id: jwt_payload.sub}, (err, user) => {
    if (err) return done(err, false);
    if (user) {
      return done(null, user);
    } else {
      return done(null, false);
    }
  });
}));

我尝试了下面的提取器,但每次都收到 404,所以我在上面创建了非常简单的提取器。如果您将令牌保存在 cookie 或类似的东西中,请按照您自己的提取器中适用的编辑进行操作,因为我发现自己更容易做到。

jwtFromRequest: ExtractJwt.fromBodyField('Token'), jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), jwtFromRequest: ExtractJwt.fromHeader("authorization"),

【讨论】:

  • 很高兴您找到了解决方案
【解决方案2】:

你打电话给jwt.verify(...) 那是为了验证一个令牌。您不需要在登录时验证令牌吗?您需要在登录时创建一个。

以您的/login 的身份逐步了解它并告诉我它是如何运行的。

 app.post("/login", function(req, res) {
      if(req.body.name && req.body.password){
        var name = req.body.name;
        var password = req.body.password;
      }
      // usually this would be a database call:
      //find user by username and password
      if( !user ){
        res.status(401).json({message:"no such user found"});
      }

      if(verify user is correct) {

        //token is created here
        var token = jwt.sign(payload, jwtOptions.secretOrKey);
        //here is the response
        res.json({message: "ok", token: token});
      } else {
        error
        res.status(401).json({message:"passwords did not match"});
      }
    });

【讨论】:

  • 我在问题的底部发布了我的新代码,因为它与您的上述登录解决方案有关。如果我要正确理解这一点,每次调用服务器时,只要像 headers: {'Authorization': jwtToken}, 那样使用 passport.authenticate(),我就需要将提供的 jwt 令牌发送到服务器?我认为将令牌保存在 state 中不是很安全,但是当我不打电话时我需要将它保存在某个地方?
  • 是的,这听起来很对。存储它的位置取决于令牌何时到期以及应用程序的需求。您可以将其存储在数据库中,我认为这没有问题。我还没有彻底检查护照的文档,但也许他们可以为你做一些这些步骤。让我知道结果如何。
  • 非常感谢!我使用 jwt 令牌没有任何问题,但不幸的是,如果我使用 app.post('/test', passport.authenticate('jwt', { session: false }), (req, res) => {console.log(req.body},我仍然会收到 401。axios.post('http://localhost:9000/login', { headers: {'authorization': token}, data: {test: 'test', Token: token} } 用于发送到服务器的内容。尝试过的提取器fromAuthHeaderAsBearerToken() fromHeader("authorization") fromBodyField('Token') 并没有任何区别。我知道您并没有在看文档,但很高兴知道。
猜你喜欢
  • 2021-08-26
  • 2018-07-23
  • 2019-10-15
  • 2017-08-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-22
相关资源
最近更新 更多