Axios发起GET请求时未携带含JWT的Cookie的问题排查
我正在搭建基于用户名密码的登录功能,后端用Node.js+Express实现,Postman测试API正常:/auth/login生成JWT并设置Cookie,能正常调用/auth/validate完成验证。但通过Astro的Login.astro页面登录时,后端日志显示登录成功且生成了Token,进入/protected页面调用/auth/validate接口时,后端始终提示Token未定义,返回403错误。
后端server.js代码
const express = require('express'); const bodyParser = require('body-parser'); // Import body-parser require('dotenv').config(); const config = process.env; const bcrypt = require('bcrypt'); const { createUser, getUser } = require('./dbController'); // const { verifyToken } = require('./middleware'); const jwt = require('jsonwebtoken'); const cookieParser = require('cookie-parser'); const cors = require('cors'); const corsOptions = { // origin: ['http://localhost:5501', 'http://127.0.0.1:5501'], // origin: true, credentials: true, // Allow credentials (cookies) methods: 'GET,POST', // Allowed HTTP methods allowedHeaders: 'Content-Type,Authorization', // Allowed headers }; const app = express(); app.use(bodyParser.json()); app.use(cors(corsOptions)); app.use(cookieParser(config.JWT_SECRET)); function verifyToken(req, res, next) { console.log('inside verifyToken, cookies: ', req.cookies); const token = req.cookies.token; console.log('inside verifyToken, token: ', token); if (!token) { console.log('no token provided'); return res .status(403) .json({ isAuthenticated: false, error: 'No token provided' }); } jwt.verify(token, config.JWT_SECRET, (err, decoded) => { if (err) { console.log('failed to authenticate token'); return res.status(401).json({ isAuthenticated: false, error: 'Failed to authenticate token', }); } console.log('decoded: ', decoded); req.user = decoded; next(); }); } app.post('/auth/signup', async (req, res) => { try { // create user const { username, password } = req.body; const encryptedPassword = await bcrypt.hash(password, 10); const user = await createUser(username, encryptedPassword); res.json(user); } catch (error) { res .status(500) .json({ error: 'An error occurred while creating the user.' }); } }); app.post('/auth/login', async (req, res) => { try { // get user const { username, password } = req.body; console.log('inside login, username: ', username); const user = await getUser(username); console.log('user: ', user); if (!user) { console.log('user not found'); res.status(401).json({ error: 'Incorrect username or password' }); return; } // check password const passwordValid = await bcrypt.compare(password, user.password); if (!passwordValid) { console.log('password invalid'); res.status(401).json({ error: 'Incorrect username or password' }); return; } const token = jwt.sign({ username: user.username }, config.JWT_SECRET, { expiresIn: '1h', }); console.log('token: ', token); // Set the JWT token in a cookie res .status(200) .cookie('token', token, { httpOnly: true, secure: true, signed: true, }) .redirect('http://localhost:3000/protected'); // .json({ message: 'Login successful' }); // Send a success response } catch (error) { res.status(500).json({ error: 'An error occurred while logging in.' }); } }); app.get('/auth/validate', verifyToken, (req, res) => { res.status(200).json({ isAuthenticated: true, user: req.user }); }); app.listen(config.PORT, () => { console.log(`Server listening on port ${config.PORT}`); });
前端Login.astro代码
--- import axios from 'axios'; if (Astro.request.method === 'POST') { try { const data = await Astro.request.formData(); const username = data.get('username'); const password = data.get('password'); console.log('username:', username); console.log('password:', password); const loginData = { username: username, password: password, }; axios .post('http://localhost:5050/auth/login', loginData) .then((response) => { if (response.status === 200) { console.log('Login successful'); // Astro.redirect('/protected'); // You can handle the redirect or other actions here } else { console.error('Login failed with status:', response.status); } }) .catch((error) => { console.error('Error:', error); }); } catch (error) { if (error instanceof Error) { console.error(error.message); } } } --- <h1>Register</h1> <form method="POST"> <label> Username: <input type="text" name="username" required /> </label> <label> Password: <input type="password" name="password" required minlength="4" /> </label> <button>Submit</button> </form>
protected.astro代码
--- import Layout from '../layouts/Layout.astro'; // import Card from '../components/Card.astro'; import Login from '../components/Login.astro'; import axios from 'axios'; axios.defaults.withCredentials = true; axios .get('http://localhost:5050/auth/validate') .then((response) => { console.log(response.data.isAuthenticated); if (response.data.isAuthenticated) { // window.location.href = '/dashboard'; } else { Astro.redirect('/'); } }) .catch((error) => { console.error('Error checking authentication status:', error); }); --- <Layout title="Welcome to Astro."> <main> <p>You are logged in!</p> </main> </Layout> <style></style>
问题排查与修复方案
1. Cookie配置错误
secure: true冲突:本地开发用HTTP协议时,secure: true会让浏览器拒绝保存Cookie,需改为环境判断:secure: process.env.NODE_ENV === 'production',仅生产环境启用。- 缺少
sameSite属性:跨域场景下需设置该属性,开发环境用sameSite: 'lax',生产环境配合secure: true设为sameSite: 'none',否则浏览器不会携带Cookie。
修改后端login接口的Cookie配置:
res.status(200) .cookie('token', token, { httpOnly: true, secure: process.env.NODE_ENV === 'production', signed: true, sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax', maxAge: 3600000 // 与JWT有效期保持一致 }) .redirect('http://localhost:3000/protected');
2. CORS配置不完整
当前CORS的origin被注释,需明确指定前端域名:
const corsOptions = { origin: 'http://localhost:3000', // Astro前端地址 credentials: true, methods: 'GET,POST', allowedHeaders: 'Content-Type,Authorization', };
3. Astro前端请求逻辑问题
- Login.astro未携带credentials:axios请求需添加
withCredentials: true,否则浏览器不保存Cookie:
axios.post('http://localhost:5050/auth/login', loginData, { withCredentials: true })
- protected.astro服务器端请求无法获取Cookie:Astro组件脚本(
---内)在服务端执行,无法读取浏览器Cookie,需将认证逻辑移到客户端脚本:
修改protected.astro:
--- import Layout from '../layouts/Layout.astro'; --- <Layout title="Welcome to Astro."> <main> <p id="auth-status">Checking authentication...</p> </main> </Layout> <script> import axios from 'axios'; axios.defaults.withCredentials = true; async function checkAuth() { try { const response = await axios.get('http://localhost:5050/auth/validate'); if (response.data.isAuthenticated) { document.getElementById('auth-status').textContent = 'You are logged in!'; } else { window.location.href = '/'; } } catch (error) { console.error('Authentication failed:', error); window.location.href = '/'; } } checkAuth(); </script> <style></style>
4. 登录流程优化
后端返回redirect会导致axios自动跟随重定向,Cookie保存受跨域影响,建议后端返回JSON,由前端处理跳转:
修改后端login接口:
res.status(200) .cookie('token', token, { httpOnly: true, secure: process.env.NODE_ENV === 'production', signed: true, sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax', maxAge: 3600000 }) .json({ message: 'Login successful' });
Login.astro中处理跳转:
axios.post('http://localhost:5050/auth/login', loginData, { withCredentials: true }) .then((response) => { if (response.status === 200) { console.log('Login successful'); window.location.href = '/protected'; } })
内容的提问来源于stack exchange,提问作者vveil
相关产品推荐
相关产品推荐

