api.example.com
,客户端为
example.com
)后,我开始在经过 JWT 验证的 API 请求上收到
401 Unauthorized
错误。我认为 JWT (
access_token_client
) 的 cookie 没有在浏览器中设置。另外,当我检查 Chrome 开发者工具 > 应用程序 > 存储 > Cookies >www.example.com 时,
access_token_client
不存在。首先,我尝试使用
credentials: true
在我的 server/index.js 中配置 CORS。我还允许起源
http://example.com
、
http://www.example.com
、
http://admin.example.com
。我还编辑了登录后端 API 控制器,因为这是设置 cookie 并将 JWT 签名给用户的地方:
export const signin = async (req, res, next) => {
try {
const user = await User.findOne({ username: req.body.username })
if (!user) return next(createError(404, "User not found!"))
const isCorrect = await bcrypt.compare(req.body.password, user.password)
if (!isCorrect) return next(createError(400, "Wrong username or password!"))
const token = jwt.sign({id: user._id}, process.env.JWT)
const {password, ...others} = user._doc
res.cookie("access_token_client", token, {
httpOnly: true,
secure: false, // Set to false because I'm on HTTP not HTTPS
sameSite: 'none',
domain: '.example.com',
maxAge: 24 * 60 * 60 * 1000 // 24 hours
}).status(200)
.json(others)
console.log('Set-Cookie header:', res.getHeader('Set-Cookie'));
} catch(err) {
next(err)
}
}
然后我编辑了服务器的 NGINX 配置:
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:8800;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header Cookie $http_cookie;
add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since';
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
}
}
在我的前端,特别是在 Login.jsx 中,我还在 axios 实例中添加了 withCredentials: true
,如下所示:
const axiosInstance = axios.create({
baseURL:process.env.REACT_APP_API_URL, // http://api.example.com/api
withCredentials: true
});
const handleLogin = async (e) => {
e.preventDefault();
dispatch(loginStart());
try {
if (!username || !password) {
throw new Error("Username and password are required");
}
const res = await axiosInstance.post("/auth/signin", {username, password});
dispatch(loginSuccess(res.data));
navigate('/dashboard');
} catch(err) {
dispatch(loginFailure());
setError(err.response ? err.response.data.message : err.message);
}
}
这是一个具有 verifyToken 中间件的 API 示例:控制器:
export const getProgresses = async (req, res, next) => {
const { userId } = req.params;
if (userId !== req.user.id) {
return next(createError(403, "Authorization error!"));
}
try {
// Find all progress records for the current user
const progresses = await Progress.find({ userId })
.populate({
path: 'unitId',
select: 'title image',
})
.populate({
path: 'moduleId',
select: 'title courseId',
populate: {
path: 'courseId',
select: 'title',
}
})
.sort({ updatedAt: -1 });
// Map progresses to include courseTitle from moduleId.courseId
const progressesWithCourseTitle = progresses.map(progress => ({
...progress.toObject(),
courseTitle: progress.moduleId.courseId.title
}));
res.status(200).json(progressesWithCourseTitle);
} catch (error) {
next(error);
}
};
路线:
router.get('/:userId', verifyToken, getProgresses)
然后这是我的 verifyToken.js 中间件:
import jwt from 'jsonwebtoken'
import { createError } from './error.js'
export const verifyToken = (req, res, next) => {
const token = req.cookies.access_token_client
if (!token) {
console.log('No token found in cookies or headers');
return next(createError(401, "You are not authenticated!"))
}
console.log('Token found:', token);
jwt.verify(token, process.env.JWT, (err, user) => {
if(err) {
console.log('Token verification failed:', err);
return next(createError(403, "Invalid token!"))
}
console.log('Token verified successfully. User:', user);
req.user = user
next()
})
}
我能够登录/登录我的网络应用程序。因为我的登录后端 API 中有这个 console.log('Set-Cookie header:', res.getHeader('Set-Cookie'));
,所以当我在服务器中
pm2 logs
时,我能够看到令牌。但每当我发出 API 请求并首先验证令牌的中间件时,我都会收到 401 未经授权的错误。经过检查,我认为收到此错误的原因是因为它首先没有要验证的令牌。在后端的
verifyToken
中间件中,找不到cookie(
req.cookies
为空)。什么可能导致浏览器中未设置 JWT cookie?任何帮助将不胜感激!谢谢!
我的前端和后端位于不同的域,现代浏览器需要跨域 cookie 的“安全”标志。仅当域(前端和后端)通过 HTTPS(而非 HTTP)提供服务时,才能使用“安全”标志。因此,通过 HTTPS 为我的域名提供服务最终解决了我的问题。