Я создаю веб-приложение BLOB-объектов с использованием Express.js, где пользователи могут входить в систему и получать доступ к защищенным маршрутам. Я настроил управление сеансами с помощью экспресс-сессии и аутентификацию с помощью паспорта-локального. Однако, когда я пытаюсь получить доступ к маршруту /home после входа в систему, я постоянно получаю ошибку «401 Несанкционировано». Он может ненадолго открыться примерно на 1-2 секунды, а затем направить меня в /login.
import express from 'express';
import bodyParser from 'body-parser';
import cors from 'cors';
import pg from 'pg';
import { fileURLToPath } from 'url';
import path from 'path';
import bcrypt from 'bcrypt';
import passport from 'passport';
import { Strategy } from 'passport-local';
import session from 'express-session';
import dotenv from 'dotenv';
dotenv.config();
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
let { PGHOST, PGDATABASE, PGUSER, PGPASSWORD, ENDPOINT_ID } = process.env;
PGPASSWORD = decodeURIComponent(PGPASSWORD);
const pool = new pg.Pool({
user: PGUSER,
host: PGHOST,
database: PGDATABASE,
password: PGPASSWORD,
port: 5432,
ssl: {
rejectUnauthorized: false,
},
connectionTimeoutMillis: 3000,
});
const app = express();
const PORT = process.env.PORT || 5000;
const saltRounds = 10;
app.use(cors({
origin: 'https://blog-t7q7.onrender.com',
credentials: true
}));
app.use(bodyParser.json({ limit: '50mb' }));
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));
app.use(session({
secret: 'TOPSECRETWORD',
resave: false,
saveUninitialized: false,
cookie: {
secure: true,
sameSite: 'none',
httpOnly: true,
}
}));
app.use(passport.initialize());
app.use(passport.session());
const isAuthenticated = (req, res, next) => {
if (res.isAuthenticated()) {
return next();
} else {
res.status(401).json({ error: 'Unauthorized' });
}
};
app.get("/home", isAuthenticated, async (req, res) => {
try {
const result = await pool.query('SELECT post_title, post_content FROM userposts');
res.status(200).json(result.rows);
} catch (error) {
console.error('Error fetching posts:', error);
res.status(500).json({ error: 'Internal Server Error' });
}
});
app.post("/register", async (req, res) => {
const { username, userpassword } = req.body;
try {
const userCheckResult = await pool.query('SELECT * FROM blog WHERE username = $1', [username]);
if (userCheckResult.rows.length > 0) {
return res.status(400).send({ error: "Username already taken" });
}
const hashedPassword = await bcrypt.hash(userpassword, saltRounds);
const result = await pool.query('INSERT INTO blog (username, userpassword) VALUES ($1, $2) RETURNING *', [username, hashedPassword]);
const user = result.rows[0];
req.login(user, (err) => {
if (err) {
console.info(err);
res.status(500).send({ error: "Registration failed" });
} else {
res.status(200).send({ message: "User registered successfully" });
}
});
} catch (error) {
console.error("Error inserting user:", error);
res.status(500).send({ error: "Registration failed" });
}
});
app.post("/login", (req, res, next) => {
passport.authenticate("local", (err, user, info) => {
if (err) {
console.error('Error during authentication:', err);
return res.status(500).send({ error: "Internal Server Error" });
}
if (!user) {
console.info('Authentication failed:', info);
return res.status(401).send({ error: "Invalid username or password" });
}
req.logIn(user, (err) => {
if (err) {
console.error('Error logging in user:', err);
return res.status(500).send({ error: "Internal Server Error" });
}
return res.status(200).send({ message: "User logged in successfully" });
});
})(req, res, next);
});
app.post("/postblog", isAuthenticated, async (req, res) => {
const { postTitle, postContent } = req.body;
try {
await pool.query('INSERT INTO userposts (post_title, post_content) VALUES ($1, $2)', [postTitle, postContent]);
res.status(200).send({ message: "Post inserted successfully" });
} catch (error) {
console.error("Error inserting post:", error);
res.status(500).send({ error: "Error inserting post" });
}
});
app.post("/post", isAuthenticated, async (req, res) => {
const { postTitle } = req.body;
console.info('Received request to fetch post:', postTitle);
try {
const result = await pool.query('SELECT * FROM userposts WHERE post_title = $1', [postTitle]);
console.info('Query result:', result.rows);
if (result.rows.length > 0) {
const post = result.rows[0];
res.status(200).send({ post });
} else {
res.status(404).send({ error: "Post not found" });
}
} catch (error) {
console.error("Error fetching post:", error);
res.status(500).send({ error: "Error fetching post" });
}
});
passport.use(new Strategy(
{
usernameField: 'username',
passwordField: 'userpassword'
},
async (username, password, cb) => {
try {
const result = await pool.query('SELECT * FROM blog WHERE username = $1', [username]);
if (result.rows.length > 0) {
const user = result.rows[0];
const storedHashedPassword = user.userpassword;
bcrypt.compare(password, storedHashedPassword, (err, isMatch) => {
if (err) {
return cb(err);
}
if (isMatch) {
return cb(null, user);
} else {
return cb(null, false, { message: 'Incorrect username or password.' });
}
});
} else {
return cb(null, false, { message: 'Incorrect username or password.' });
}
} catch (error) {
console.error("Error logging in user:", error);
return cb(error);
}
}
));
passport.serializeUser((user, cb) => {
cb(null, user.id);
});
passport.deserializeUser(async (id, cb) => {
try {
const result = await pool.query('SELECT * FROM blog WHERE id = $1', [id]);
if (result.rows.length > 0) {
cb(null, result.rows[0]);
} else {
cb(new Error('User not found'));
}
} catch (error) {
cb(error);
}
});
app.listen(PORT, () => {
console.info(`Server is running on port ${PORT}`);
});
это мой index.js
import React, { useState } from "react";
import axios from 'axios';
import { useNavigate } from 'react-router-dom';
import './Login.css'; // Import the CSS file
function Login() {
const [name, setName] = useState("");
const [password, setPassword] = useState("");
const navigate = useNavigate();
const handleNameChange = (event) => {
setName(event.target.value);
}
const handlePasswordChange = (event) => {
setPassword(event.target.value);
}
const loginUser = async (event) => {
event.preventDefault();
try {
const response = await axios.post('https://blog-backend-khj7.onrender.com/login',
{ username: name, userpassword: password },
{ withCredentials: true });
if (response.status === 200) {
navigate('/home');
}
} catch (error) {
if (error.response) {
if (error.response.status === 400 || error.response.status === 401) {
alert("Invalid username or password");
} else {
console.error('Error logging in user:', error);
alert("There was an error logging in the user");
}
} else {
console.error('Error logging in user:', error);
alert("There was an error logging in the user");
}
}
}
return (
<div className = "login-background">
<div className = "login-container">
<h1>Login</h1>
<form onSubmit = {loginUser}>
<input type = "text" onChange = {handleNameChange} name = "username" value = {name} placeholder = "Username" required />
<input type = "password" onChange = {handlePasswordChange} name = "userpassword" value = {password} placeholder = "Password" required />
<button type = "submit">Submit</button>
</form>
</div>
</div>
);
}
export default Login;
логин.jsx
import React, { useState } from "react";
import axios from 'axios';
import { useNavigate } from 'react-router-dom';
import './Register.css'; // Import the CSS file
function Register() {
const [name, setName] = useState("");
const [password, setPassword] = useState("");
const navigate = useNavigate();
function handleNameChange(event) {
setName(event.target.value);
}
function handlePasswordChange(event) {
setPassword(event.target.value);
}
async function registerUser(event) {
event.preventDefault();
try {
const response = await axios.post('https://blog-backend-khj7.onrender.com/register',
{ username: name, userpassword: password },
{ withCredentials: true }
);
if (response.status === 200) {
navigate('/home');
}
} catch (error) {
if (error.response && error.response.status === 400) {
alert("Username already exists");
} else {
console.error('There was an error registering the user:', error);
}
}
}
function goToLogin() {
navigate('/login');
}
return (
<div className = "register-background">
<h1 className = "register-header">Welcome to blog</h1>
<div className = "register-container">
<h1>Register</h1>
<form onSubmit = {registerUser}>
<input type = "text" onChange = {handleNameChange} value = {name} placeholder = "Username" />
<input type = "password" onChange = {handlePasswordChange} value = {password} placeholder = "Password" />
<button type = "submit">Submit</button>
</form>
</div>
<h3>Already registered?</h3>
<h4 onClick = {goToLogin} className = "blog-go-login">Login</h4>
</div>
);
}
export default Register;
регистр.jsx
Сайт работает! Вы также можете проверить https://blog-t7q7.onrender.com/
github https://github.com/AMEYSATI/Блог
Дело в инструментах разработчика, когда я правильно вхожу в систему, сеанс cookie передается, но я думаю, он не получен /home или что-то в этом роде.
Пожалуйста, помогите мне, так как это тоже для меня ново.
🤔 А знаете ли вы, что...
Node.js активно используется для создания серверных приложений на стороне бэкенда в стеке MEAN (MongoDB, Express.js, Angular, Node.js).
Итак, проблема заключалась в том, что до того, как я разместил свой проект на рендеринге, он работал, то есть сеанс успешно создавался, и файлы cookie передавались с каждым запросом, но после того, как он был размещен, файлы cookie передавались после регистрации и входа в систему, но не проходили аутентификацию.
Решение состоит в том, что вам просто нужно написать app.set('trust proxy', 1)
Установив app.set('trust proxy', 1), вы включили Express, чтобы доверять первому прокси в цепочке заголовков X-Forwarded-*, что часто необходимо, когда ваше приложение находится за прокси-сервером, например, используемым некоторыми хостинг-провайдерами. . https://expressjs.com/en/resources/middleware/session.html
Проще говоря, пользователь пытается войти в систему или запросить определенную страницу, которую вы аутентифицировали. Сначала запрос поступает на прокси-сервер (например, сервер, используемый Render, Heroku, Netlify). Прокси-сервер перенаправляет запрос на ваше приложение Express. Express не доверяет информации от прокси-сервера. Он считает, что запрос исходит от прокси-сервера, а не от фактического пользователя. Безопасные файлы cookie и IP-адреса клиентов обрабатываются неправильно, поскольку Express не доверяет прокси-серверу.
Таким образом, установив настройку app.set('trust proxy', 1), Express доверяет первому прокси в цепочке. Он видит исходную информацию пользователя (например, IP-адрес), предоставленную прокси. Безопасные файлы cookie обрабатываются правильно, поскольку Express знает исходный соединение безопасно.