Node.js+Angular集成Passport登录功能故障排查求助
点击登录按钮后UI无任何反应,无法跳转到首页。核心问题出在Passport配置环节:代码无法执行完findOne()方法,后续逻辑及console.log均未触发。控制台仅输出Sequelize警告:传入模型korisnik的查询属性(email)未设置where条件,执行了无限制的SELECT语句;即使手动添加where子句,也仅显示SELECT语句,无后续执行。
相关代码
login.component.ts
signupForm!: FormGroup; constructor(private router:Router, private userService: UserService) { } ngOnInit(): void { this.signupForm = new FormGroup({ 'email': new FormControl(null,[Validators.required]), // 'email': new FormControl(null,[Validators.required,Validators.email]), 'password': new FormControl(null,Validators.required) }) } onSubmit(){ this.userService.login(JSON.stringify(this.signupForm.value)).subscribe(data=>{console.log('Success');this.router.navigate(['/home']);}, error=>console.log(error)) }
前端Service
return this.http.post(this.apiUrlL,body,{ observe:'body', withCredentials:true, headers:new HttpHeaders().append('Content-Type','application/json') });
前端Model
export class UserModel { public id!: number; public ime!: string; public prezime!: string; public email!: string; public password!: string; constructor() {} }
Passport-config(问题核心)
var passport = require("passport"), LocalStrategy = require("passport-local").Strategy; const UserCont = require("../controller/user-cont"); var User = require("../model/user_data"); passport.use( "local", new LocalStrategy( { usernameField: "email", passwordField: "password", }, function (username, password, done) { User.User.findOne({ email: username }, function (err, user) { if (err) { return done(err, { message: "testcase." }); } if (!user) { return done(null, false, { message: "Incorrect username." }); } if (!UserCont.isValid(user.email, password)) { return done(null, false, { message: "Incorrect password." }); } console.log("passport user", username); return done(null, user); }); } ) ); passport.serializeUser(function (user, done) { done(null, user.id); }); passport.deserializeUser(function (id, done) { User.User.findById(id, function (err, user) { done(err, user); }); });
后端login.js
var User = require("../model/user_data"); var passport = require("passport"); module.exports.loginUser = (req, res, next) => { passport.authenticate("local", function (err, user, info) { if (err) { return res.status(501).json(err); } if (!user) { return res.status(501).json(info); } req.logIn(user, function (err) { if (err) { return res.status(501).json(err); } return res.status(200).json({ message: "Login Success" }); }); })(req, res, next); };
后端User-controller
module.exports.isValid = (email, newPass) => { const salt = crypto.genSalt(10); const hashPass = crypto.hash(newPass, salt); User.User.findOne({ email: email }).then((user) => { crypto.compare(user.password, hashPass, function (err, ifValid) { if (ifValid == true) { console.log(ifValid); return true; } }); }); };
后端User-routing文件
const express = require("express"); const Login = require("../common/login"); const router = express.Router(); router.post("/login", Login.loginUser); module.exports = router;
后端App.js(Passport相关部分)
//passport var passport = require("passport"); var session = require("express-session"); app.use( session({ name: "Session ID", resave: false, saveUninitialized: false, secret: "secret", cookie: { maxAge: 36000000, httpOnly: false, secure: false, }, }) ); require("./common/passport-config"); app.use(passport.initialize()); app.use(passport.session()); // app.use(express.json()); app.use("/api", userRouting);
后端DB Model
const Sequelize = require("sequelize"); const sequelize = require("./../common/db-config"); const Model = Sequelize.Model; //User class User extends Model {} User.init( { id: { type: Sequelize.INTEGER, allowNull: false, primaryKey: true, autoIncrement: true, field: "id", }, ime: { type: Sequelize.STRING, allowNull: false, field: "ime", }, prezime: { type: Sequelize.STRING, allowNull: false, field: "prezime", }, email: { type: Sequelize.STRING, allowNull: false, unique: true, field: "email", }, password: { type: Sequelize.STRING, allowNull: false, field: "sifra", }, }, { sequelize, modelName: "korisnik", tableName: "korisnik", timestamps: false, } ); module.exports = { User };
问题排查与修复方案
1. Sequelize 查询语法错误(核心问题)
Sequelize的findOne()需要将查询条件放在where对象中,原代码直接传{ email: username }会被当成查询属性而非条件,导致无限制查询且回调不触发。
修复Passport-config中的findOne:
// 原代码 User.User.findOne({ email: username }, function (err, user) { ... }) // 修改后 User.User.findOne({ where: { email: username } }, function (err, user) { ... })
同时deserializeUser中的findById已废弃,改为findByPk:
passport.deserializeUser(function (id, done) { User.User.findByPk(id, function (err, user) { done(err, user); }); });
2. isValid函数异步逻辑错误
crypto.genSalt、crypto.hash、crypto.compare都是异步方法,原代码同步调用会返回Promise而非实际值,且整个函数没有返回值,导致Passport中!UserCont.isValid()永远为true,直接触发密码错误分支。
修复User-controller中的isValid:
const crypto = require('crypto'); // 记得导入crypto const User = require("../model/user_data"); module.exports.isValid = async (email, newPass) => { try { // 用promise版本的crypto方法 // 验证密码时不需要重新哈希,直接用明文和数据库中的哈希对比 const user = await User.User.findOne({ where: { email: email } }); return await crypto.promises.compare(newPass, user.password); } catch (err) { console.error(err); return false; } };
3. Passport LocalStrategy适配异步isValid
因为isValid改成了异步函数,LocalStrategy的回调需要用async/await:
passport.use( "local", new LocalStrategy( { usernameField: "email", passwordField: "password", }, async function (username, password, done) { try { const user = await User.User.findOne({ where: { email: username } }); if (!user) { return done(null, false, { message: "Incorrect username." }); } const isValid = await UserCont.isValid(user.email, password); if (!isValid) { return done(null, false, { message: "Incorrect password." }); } console.log("passport user", username); return done(null, user); } catch (err) { return done(err, { message: "Server error." }); } } ) );
4. 前端数据传递错误
前端login方法中用JSON.stringify把表单值转成字符串,但后端用express.json()解析JSON格式的请求体,直接传对象即可,否则后端无法正确解析email和password。
修复login.component.ts中的onSubmit:
onSubmit(){ this.userService.login(this.signupForm.value).subscribe( data => { console.log('Success'); this.router.navigate(['/home']); }, error => console.error(error) ) }
内容的提问来源于stack exchange,提问作者Drexx_Dusan

