所以我是 MERN 堆栈的初学者。目前正在做一个项目。我在expressjs中使用passportjs进行身份验证。现在登录页面上的身份验证(REACT)已顺利完成,但当到达下一页时,它会返回到未授权状态,我无法访问数据。
控制器
import express from 'express';
import session from 'express-session';
import passport from 'passport';
import LocalStrategy from 'passport-local';
import { PoliceStation } from '../Models/policestation.model.js';
class PoliceController {
constructor() {
this.router = express.Router();
this.initializePassport();
this.initializeRoutes();
}
initializePassport() {
console.log('Initializing passport')
passport.use(
new LocalStrategy(async (username, password, done) => {
try {
console.log('Incoming username:', username);
console.log('Incoming password:', password);
const user = await PoliceStation.findOne({ name: username, password });
if (!user) {
console.log('User not found');
return done(null, false, { message: 'Incorrect username or password' });
}
console.log('User found:', user);
return done(null, user);
} catch (error) {
console.error('Error in LocalStrategy:', error);
return done(error);
}
})
);
passport.serializeUser((user, done) => {
console.log('Serialize User:', user.id);
done(null, user.id);
});
passport.deserializeUser(async (id, done) => {
console.log('Deserialize User ID:', id);
try {
const user = await PoliceStation.findById(id);
console.log('Deserialized User:', user);
done(null, user);
} catch (error) {
console.error('Deserialize User Error:', error);
done(error);
}
});
}
initializeRoutes() {
this.router.use(
session({
secret: 'your-secret-key',
resave: false,
saveUninitialized: true,
})
);
this.router.use(passport.initialize());
this.router.use(passport.session());
// ...
const requireLogin = (req, res, next) => {
console.log('Authentication Status:', req.isAuthenticated());
console.log('User Object:', req.user);
if (req.isAuthenticated()) {
next();
} else {
res.status(401).send('Unauthorized');
}
};
// Inside your server route handling the login
this.router.post('/login', passport.authenticate('local'), (req, res) => {
if (req.isAuthenticated()) {
// Authentication successful
const user = req.user;
console.log('Authenticated User:', user);
// Log session data
console.log('Session Data:', req.session);
res.status(200).json({ message: 'Login successful', user });
} else {
// Authentication failed
console.log('Authentication failed');
res.status(401).json({ message: 'Login failed' });
}
});
// ...
this.router.get('/checklogin', (req, res) => {
if (req.isAuthenticated()) {
console.log('User is authenticated:', req.user);
res.json({ loggedIn: true, user: req.user });
} else {
console.log('User is not authenticated');
res.json({ loggedIn: false });
}
});
}
export default PoliceController;
登录.JSX
const Login = ({ setLoggedInUser }) => {
const navigate = useNavigate();
const [name, setname] = useState('');
const [password, setPassword] = useState('');
const handleLogin = async (e) => {
e.preventDefault();
try {
const username = name;
const response = await axios.post('http://localhost:5000/police/login', { username, password });
const { user } = response.data;
console.log('Login Response:', response);
Cookies.set('userId', user._id, { expires: 7 });
Cookies.set('token', response.data.token, { expires: 7 });
setLoggedInUser(user);
// Check login status after setting cookies
const checkLoginResponse = await axios.get('http://localhost:5000/police/checklogin');
console.log('Response Data:', checkLoginResponse.data);
console.log('Full Response:', checkLoginResponse);
navigate('/home', { replace: true });
if (checkLoginResponse.data.loggedIn) {
// Redirect or perform other actions after successful login
navigate('/home', { replace: true });
} else {
console.error('Login failed. Response:', checkLoginResponse.data);
}
} catch (error) {
console.error('Login error:', error);
console.log('Complete error object:', error);
}
};
export default Login;
HOME.JSX
const Home = () => {
const [policeOfficers, setPoliceOfficers] = useState([]);
const [error, setError] = useState(null);
console.log("This is cookie on home",Cookies)
console.log("This is cookie id in home component",Cookies.get('userId'));
console.log("Cookie TOken on page no 2: ",Cookies.get)
useEffect(() => {
const fetchData = async () => {
try {
const userId = Cookies.get('userId');
const response = await axios.get(`http://localhost:5000/police/officer/${userId}`);
setPoliceOfficers(response.data.policeOfficers);
} catch (error) {
if (error.response && error.response.status === 401) {
setError('Unauthorized. Please log in.');
} else {
setError('Error fetching police officers.');
}
}
};
fetchData();
}, []); // Empty dependency array means this effect runs once when the component mounts
}
export default Home;
所以我尝试了控制台日志记录,这就是结果。
前端控制台
Login Response: {data: {…}, status: 200, statusText: 'OK', headers: AxiosHeaders, config: {…}, …}
Login.jsx:26 Response Data: {loggedIn: false}
Login.jsx:27 Full Response: {data: {…}, status: 200, statusText: 'OK', headers: AxiosHeaders, config: {…}, …}
Home.jsx:10 This is cookie on home {attributes: {…}, converter: {…}}
Home.jsx:11 This is cookie id in home component 646e2f653433e58b4b36bbc9
Home.jsx:12 Cookie TOken on page no 2: ƒ (name) {
if (typeof document === 'undefined' || arguments.length && !name) {
return;
}
// To prevent the for loop in the first place assign an empty array
// in case there are …
Home.jsx:10 This is cookie on home {attributes: {…}, converter: {…}}
Home.jsx:11 This is cookie id in home component 646e2f653433e58b4b36bbc9
Home.jsx:12 Cookie TOken on page no 2: ƒ (name) {
if (typeof document === 'undefined' || arguments.length && !name) {
return;
}
// To prevent the for loop in the first place assign an empty array
// in case there are …
xhr.js:251
GET http://localhost:5000/police/officer/646e2f653433e58b4b36bbc9 401 (Unauthorized)
dispatchXhrRequest @ xhr.js:251
xhr @ xhr.js:49
dispatchRequest @ dispatchRequest.js:51
request @ Axios.js:148
Axios.<computed> @ Axios.js:174
wrap @ bind.js:5
fetchData @ Home.jsx:19
(anonymous) @ Home.jsx:30
commitHookEffectListMount @ react-dom.development.js:23150
commitPassiveMountOnFiber @ react-dom.development.js:24926
commitPassiveMountEffects_complete @ react-dom.development.js:24891
commitPassiveMountEffects_begin @ react-dom.development.js:24878
commitPassiveMountEffects @ react-dom.development.js:24866
flushPassiveEffectsImpl @ react-dom.development.js:27039
flushPassiveEffects @ react-dom.development.js:26984
(anonymous) @ react-dom.development.js:26769
workLoop @ scheduler.development.js:266
flushWork @ scheduler.development.js:239
performWorkUntilDeadline @ scheduler.development.js:533
Home.jsx:10 This is cookie on home {attributes: {…}, converter: {…}}
Home.jsx:11 This is cookie id in home component 646e2f653433e58b4b36bbc9
后端控制台
User found: {
location: {
type: 'Point',
coordinates: [ 53.4808, 2.2426 ]
},
_id: new ObjectId("646e2f653433e58b4b36bbc9"),
name: 'MDA',
address: 'MANCHESTER',
phone: 519255339,
password: '12345678',
__v: 5,
policeOfficers: [
new ObjectId("646e3bf76c974f42bcbb055d"),
new ObjectId("646f67acdff8e98bf7fcf530"),
new ObjectId("646fc7aa88960bcfdc00352a"),
new ObjectId("646fc7b788960bcfdc003532"),
new ObjectId("6473905b525377ad83c76d27")
]
}
Serialize User: 646e2f653433e58b4b36bbc9
Authenticated User: {
location: {
type: 'Point',
coordinates: [ 53.4808, 2.2426 ]
},
_id: new ObjectId("646e2f653433e58b4b36bbc9"),
name: 'MDA',
address: 'Manchester',
phone: 519255339,
password: '12345678',
__v: 5,
policeOfficers: [
new ObjectId("646e3bf76c974f42bcbb055d"),
new ObjectId("646f67acdff8e98bf7fcf530"),
new ObjectId("646fc7aa88960bcfdc00352a"),
new ObjectId("646fc7b788960bcfdc003532"),
new ObjectId("6473905b525377ad83c76d27")
]
}
Session Data: Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true },
passport: { user: '646e2f653433e58b4b36bbc9' }
}
User is not authenticated
Authentication Status: false
User Object: undefined
Authentication Status: false
User Object: undefined
该问题似乎与跨请求的会话持久性有关。这是问题的诊断:
成功登录后,您的用户将通过身份验证,并在服务器上生成会话数据。此会话数据包括一个唯一的会话 ID,应使用 cookie 在客户端和服务器之间来回传递。当服务器收到具有有效会话 ID 的请求时,它会识别经过身份验证的用户并可以授予访问权限。
但是,在对
/police/officer/${userId}
的后续请求中,身份验证状态为 false
,这表明会话未跨请求持久保存。最常见的原因是会话 ID cookie 未传回服务器或未被服务器识别。
以下是一些排除故障和解决问题的步骤:
您的前端 axios 请求应配置为随请求发送凭据(cookie)。更新您的 axios 请求以包含
withCredentials: true
:
await axios.post('http://localhost:5000/police/login', { username, password }, { withCredentials: true });
// ...
await axios.get('http://localhost:5000/police/checklogin', { withCredentials: true });
// ...
await axios.get(`http://localhost:5000/police/officer/${userId}`, { withCredentials: true });
在服务器端,如果您使用 CORS,请确保您的 CORS 配置设置为接受和处理凭据:
import cors from 'cors';
app.use(cors({
origin: 'http://localhost:3000', // your React app's domain
credentials: true
}));
默认会话存储
memoryStore
是为开发而设计的,不适合生产,因为它在大多数情况下会泄漏内存,并且无法扩展到单个进程。您应该使用更强大的会话存储,例如 Redis 的 connect-redis
或连接到您的数据库。
确保
express-session
中的会话cookie设置正确:
this.router.use(
session({
secret: 'your-secret-key',
resave: false,
saveUninitialized: true,
cookie: {
httpOnly: true,
secure: false, // set to true if using HTTPS
maxAge: 7 * 24 * 60 * 60 * 1000 // 7 days in milliseconds
}
})
);
始终关注服务器日志。查找与会话创建、检索或身份验证相关的任何潜在错误或异常行为。
通过执行上述步骤,您应该能够识别并修复 MERN 应用程序中的会话持久性问题。