我正在通过构建一个小项目来学习身份验证和 JWT。但在用户使用 Google OAuth 登录后,我一直试图将 JWT(JSON Web 令牌)发送回前端。问题是,当后端登录后重定向时,它会丢失包含 JWT 的标头。
我使用的是:React 和 Vite 作为前端,Node.js 和 Express.js 作为后端。对于 Google 身份验证,我使用 Passport.js 中名为 Passport-google-oauth20 的策略,并进行了一些调整。
这是我的 Google 身份验证策略和路由的后端代码片段:
// Backend Code Snippet
passport.use(new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: process.env.FRONTEND_URL + "/auth/google/callback",
},
async (accessToken, refreshToken, profile, cb) => {
try {
// Logic to create or find user
// ...
return cb(null, user);
} catch (error) {
return cb(error);
}
}
));
router.get("/google", passport.authenticate("google", { scope: ["profile"] }));
router.get(
"/google/callback",
passport.authenticate("google", {
session: false,
failureRedirect: process.env.FRONTEND_URL + "/auth/login",
}),
authController.google_callback
);
这是我生成 JWT 并在 google_callback 函数中设置标头的方法:
// Google Callback Implementation
exports.google_callback = asyncHandler(async (req, res, next) => {
let user = req.user;
!user && res.redirect(frontend_url + "/auth/login");
const opts = {};
opts.expiresIn = 3600; // 1 hour
const token = jwt.sign({ user }, secret, opts);
res.setHeader("authorization", `Bearer ${token}`);
res.setHeader("Access-Control-Expose-Headers", "authorization");
// res.redirect(frontend_url + "/");
res.status(200).json({
status: "success",
token: token,
});
});
尽管我付出了努力,但我还没有找到一种方法可以将 JWT 发送回前端而不丢失它。我能够访问 Google 身份验证的唯一方法是从前端重定向,如下所示:
// Frontend Code Snippet
<a href={import.meta.env.VITE_BACKEND_URL + "/auth/google"}>
Continue with Google
</a>
并且使用 Axios 进行 GET 请求会导致 CORS 问题:
Access to XMLHttpRequest at 'backend_url/auth/google' from origin 'frontend' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
如果有人有将 Google OAuth 与 JWT 身份验证集成的经验,我真的很感激一些指导。预先感谢您的帮助。
我预计将 JWT 与重定向一起发送到响应的标头中,然后打算将其存储在我前端的本地存储中。但是,当我使用 res.redirect 时,令牌丢失了。另外,当使用 res.json 时,我面临着无法在前端重定向和存储令牌的困境。
要解决 Google OAuth 登录后将 JWT 发送回前端的问题,您需要稍微修改一下方法,以确保 JWT 正确包含在响应中。
在您的 google_callback 函数中,您当前正在响应标头中设置 JWT,并尝试发送 JSON 响应。但是,您无法在单个响应中同时重定向和发送标头。相反,您应该发送带有令牌的 JSON 响应,或者使用包含令牌的查询参数进行重定向。
以下是修改后端代码以在 JSON 响应中发送令牌的方法:
exports.google_callback = asyncHandler(async (req, res, next) => {
let user = req.user;
if (!user) {
return res.status(401).json({ error: "User not found" });
}
const opts = {};
opts.expiresIn = 3600; // 1 hour
const token = jwt.sign({ user }, secret, opts);
// Send the token in the response body
return res.status(200).json({
status: "success",
token: token,
});
});
修改后端以在响应正文中发送令牌后,您可以在前端代码中处理它。您可以使用 JavaScript 从响应中提取令牌并将其存储在本地存储中以供将来使用。
以下是如何在前端代码中处理令牌的基本示例:
fetch(`${import.meta.env.VITE_BACKEND_URL}/auth/google/callback`, {
method: "GET",
credentials: "include",
})
.then((response) => response.json())
.then((data) => {
const token = data.token;
localStorage.setItem("token", token);
})
.catch((error) => {
console.error("Error:", error);
});
确保您的前端代码安全地处理 JWT,例如在后续请求中验证它并防止 XSS 攻击。 考虑使用 HTTPS 进行后端到前端的通信,以确保令牌传输的安全性。