You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Axios发起GET请求时未携带含JWT的Cookie的问题排查

JWT Cookie在Astro前端请求中丢失导致403认证失败

我正在搭建基于用户名密码的登录功能,后端用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 01:55:00