#node.js #express #jwt #express-jwt
Вопрос:
Я создаю приложение для социальных сетей, используя стек MERN.
Я использую POSTMAN для тестирования внутреннего API.
Ниже приведен список зависимостей, т. е. файл package.json.
{
"name": "SocialMediaApp",
"version": "1.0.0",
"description": "A simple MERN-stack based social media app.",
"main": "index.js",
"scripts": {
"development": "nodemon"
},
"author": "Prithvi",
"license": "MIT",
"keywords": [
"react",
"node",
"express",
"mongodb",
"mern"
],
"dependencies": {
"@hot-loader/react-dom": "^17.0.1",
"compression": "^1.7.4",
"cookie-parser": "^1.4.5",
"cors": "^2.8.5",
"express": "^4.17.1",
"express-jwt": "^6.0.0",
"helmet": "^4.4.1",
"jshint": "^2.12.0",
"jsonwebtoken": "^8.5.1",
"loadash": "^1.0.0",
"mongodb": "^3.6.4",
"mongoose": "^5.12.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-hot-loader": "^4.13.0"
},
"devDependencies": {
"@babel/core": "^7.13.10",
"@babel/preset-env": "^7.13.10",
"@babel/preset-react": "^7.12.13",
"babel-loader": "^8.2.2",
"file-loader": "^6.2.0",
"nodemon": "^2.0.7",
"webpack": "^5.24.4",
"webpack-cli": "^4.5.0",
"webpack-dev-middleware": "^4.1.0",
"webpack-hot-middleware": "^2.25.0",
"webpack-node-externals": "^2.5.2"
}
}
На самом деле, когда я пытаюсь протестировать серверный API для запроса POST для входа в конечную http://localhost:3000/auth/signin
точку , я получаю ошибку, как показано ниже, с кодом ошибки 401 Unauthorized
.
{
"error": "Could not sign in ! :("
}
Ниже приведен файл модели пользователя, т. е., user.model.js.
import mongoose from "mongoose";
import crypto from "crypto";
const UserSchema = new mongoose.Schema({
name: {
type: String,
trim: true,
required: 'Name is required',
},
email: {
type: String,
trim: true,
unique: 'Email already exists',
match: [/. @. .. /, 'Please fill a valid email address'],
required: 'Email is required !',
},
hashed_password: {
type: String,
required: 'Password is required !',
},
salt: String,
updated: Date,
created: {
type: Date,
default: Date.now,
},
});
/**
* The password string that's provided by the user is not stored directly in the user
* document. Instead, it is handled as a virtual field.
*/
UserSchema
.virtual('password')
.set(function (password) {
this._password = password;
this.salt = this.makeSalt();
this.hashed_password = this.encryptPassword(password);
})
.get(function () {
return this._password;
});
/**
* To add validation constraints to the actual password string that's selected by the end
* user, we need to add custom validation logic and associate it with the hashed_password
* field in the schema.
*/
UserSchema.path('hashed_password').validate(function (v) {
if (this._password amp;amp; this._password.length < 6) {
this.invalidate('password', 'Password must be atleast 6 characters.');
}
if (this.isNew amp;amp; !this._password) {
this.invalidate('password', 'Password is required.');
}
}, null);
/**
* The encryption logic and salt generation logic, which are used to generate the
* hashed_password and salt values representing the password value, are defined as
* UserSchema methods.
*/
UserSchema.methods = {
authenticate: function (plainText) {
return this.authenticate(plainText) === this.hashed_password;
},
encryptPassword: function (password) {
if (!password) return '';
try {
return crypto
.createHmac('sha1', this.salt)
.update(password)
.digest('hex');
} catch (err) {
return '';
}
},
makeSalt: function () {
return Math.round(new Date().valueOf() * Math.random()) '';
},
};
export default mongoose.model('User', UserSchema);
Below is the controller file i.e., auth.controller.js.
import User from "../models/user.model";
import jwt from 'jsonwebtoken';
import expressJwt from "express-jwt";
import config from "./../../config/config";
const signin = async (req, res) => {
try {
let user = await User.findOne({
"email": req.body.email
});
if (!user) {
return res.status('401').json({
error: "User not found"
});
}
if (!user.authenticate(req.body.password)) {
return res.status('401').send({
error: "Email and passwords don't match."
});
}
const token = jwt.sign({
_id: user._id,
}, config.jwtSecret);
res.cookie('t', token, {
expire: new Date() 9999
});
return res.json({
token,
user: {
_id: user._id,
name: user.name,
email: user.email
}
});
} catch (err) {
return res.status('401').json({
error: "Could not sign in ! :("
});
}
};
const signout = (req, res) => {
res.clearCookie("t");
return res.status('200').json({
message: "signed out"
});
};
const requireSignin = expressJwt({
secret: config.jwtSecret,
userProperty: 'auth',
algorithms: ['HS256']
});
const hasAuthorization = (req, res, next) => {
const authorized = req.profile amp;amp; req.auth amp;amp; req.profile._id == req.auth._id;
if (!(authorized)) {
return res.status('403').json({
error: "User isn't authorized !"
});
}
next();
};
export default {
signin,
signout,
requireSignin,
hasAuthorization
}
And here is the routes fle i.e., auth.routes.js.
import express from "express";
import authCtrl from "../controllers/auth.controller";
const router = express.Router();
router.route('/auth/signin')
.post(authCtrl.signin);
router.route('/auth/signout')
.get(authCtrl.signout);
export default router;
Lastly, express.js file.
import express from "express";
import path from "path";
import cookieParser from "cookie-parser";
import compress from "compression";
import cors from "helmet";
import helmet from "cors";
import Template from "./../template";
import userRoutes from "./routes/user.routes";
import authRoutes from "./routes/auth.routes";
const CURRENT_WORKING_DIR = process.cwd();
const app = express();
app.use(express.json());
app.use(express.urlencoded({
extended: true
}));
app.use(cookieParser());
app.use(compress());
app.use(helmet());
app.use(cors());
app.use('/dist', express.static(path.join(CURRENT_WORKING_DIR, 'dist')));
app.use('/', userRoutes);
app.use('/', authRoutes);
app
.get("/", (req, res) => {
res.status(200).send(Template());
});
app.use((err, req, res, next) => {
if (err.name === 'UnauthorizedError') {
res.status(401).json({
"error": err.name ": " err.message
});
} else if (err) {
res.status(400).json({
"error": err.name ": " err.message
});
console.log(err);
}
});
export default app;
P. S
Я получаю следующую ошибку в терминале для user.model.js.
RangeError: Maximum call stack size exceeded
at model.authenticate (webpack://SocialMediaApp/./server/models/user.model.js?:80:17)
at model.authenticate (webpack://SocialMediaApp/./server/models/user.model.js?:80:17)
at model.authenticate (webpack://SocialMediaApp/./server/models/user.model.js?:80:17)
at model.authenticate (webpack://SocialMediaApp/./server/models/user.model.js?:80:17)
at model.authenticate (webpack://SocialMediaApp/./server/models/user.model.js?:80:17)
at model.authenticate (webpack://SocialMediaApp/./server/models/user.model.js?:80:17)
at model.authenticate (webpack://SocialMediaApp/./server/models/user.model.js?:80:17)
at model.authenticate (webpack://SocialMediaApp/./server/models/user.model.js?:80:17)
at model.authenticate (webpack://SocialMediaApp/./server/models/user.model.js?:80:17)
at model.authenticate (webpack://SocialMediaApp/./server/models/user.model.js?:80:17)
at model.authenticate (webpack://SocialMediaApp/./server/models/user.model.js?:80:17)
at model.authenticate (webpack://SocialMediaApp/./server/models/user.model.js?:80:17)
at model.authenticate (webpack://SocialMediaApp/./server/models/user.model.js?:80:17)
at model.authenticate (webpack://SocialMediaApp/./server/models/user.model.js?:80:17)
at model.authenticate (webpack://SocialMediaApp/./server/models/user.model.js?:80:17)
at model.authenticate (webpack://SocialMediaApp/./server/models/user.model.js?:80:17)
Комментарии:
1. Не могли бы вы поделиться тем, как вы называете эту услугу от почтальона? Я хочу показать, как вы передаете параметр и все такое.
2. Отправив запрос на конечную точку api, т. е.
http://localhost:3000/auth/signin
указав адрес электронной почты и пароль вместе с запросом POST .3. Не могли бы вы, пожалуйста, прикрепить экран для того же самого?
4. Я тебя не понял ! Не могли бы вы быть более конкретными ?
5. Не могли бы вы изменить эту часть
return res.status('401').json({ error: "Could not sign in ! :(" });
кода наreturn res.status('401').json({ error: err.message });
и снова выполнить запрос, чтобы мы могли лучше видеть, что происходит в контроллере.
Ответ №1:
const token = jwt.sign({ _id: user.id, }, config.jwtSecret);
здесь вы должны использовать _id: user._id
Комментарии:
1. Я все еще получаю ту же ошибку. P. S — Вы можете проверить детали вопроса, которые я обновил, добавив express.js файл.
Ответ №2:
Я думаю, что вы пытаетесь сделать что-то вроде этого: в методе аутентификации просто вызовите encryptPassword()
, а authenticate()
не повторите. Вы вызывали authenticate()
в рекурсивном цикле без контроля; это было причиной ошибки.
UserSchema.methods = {
authenticate: function (plainText) {
return this.encryptPassword(plainText) === this.hashed_password;
},
encryptPassword: function (password) {
if (!password) return '';
try {
return crypto
.createHmac('sha1', this.salt)
.update(password)
.digest('hex');
} catch (err) {
return '';
}
},
makeSalt: function () {
return Math.round(new Date().valueOf() * Math.random()) '';
},
};
Ответ №3:
Я не знаю почему, но я заинтригован import cors from 'helmet'
и import helmet from 'cors'
Изменить: Вы пытались вернуться true
в:
authenticate: function (plainText) { return this.authenticate(plainText) === this.hashed_password; }
Если ошибки больше нет, то вы возвращаетесь из рекурсивного цикла authenticate
.
Комментарии:
1. после того, как вы указали, даже я удивлен. Довольно забавно, я не заметил этой ошибки !! Я думаю, это типографская ошибка.
2. вы можете узнать подробности вопроса о проверке, которые я обновил, для получения дополнительной информации, связанной с ошибкой, которую я все еще показываю после исправления ошибок импорта.