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

Node.js中Post请求后出现401未授权错误的解决方法

问题:新注册账号登录返回401未授权错误

本地环境下登录注册功能完全正常:注册账号数据可存入数据库,重复注册会提示账号已存在,本地创建的账号能正常登录;但新注册的账号在Render部署环境登录时会返回401(未授权)错误。已在本地添加包含Token的.env文件,且在Render平台配置了对应的环境变量。


相关代码

server.js

const express = require('express');
const app = express();
const path = require('path');
const cors = require('cors');
const corsOptions = require('./config/corsOptions');
const { logger } = require('./middleware/logEvents');
const errorHandler = require('./middleware/errorHandler');
const verifyJWT = require('./middleware/verifyJWT');
const cookieParser = require('cookie-parser');
const credentials = require('./middleware/credentials');
const PORT = process.env.PORT || 3500;
// custom middleware logger
app.use(logger);

// Handle options credentials check - before CORS!
// and fetch cookies credentials requirement
app.use(credentials);

// Cross Origin Resource Sharing
app.use(cors(corsOptions));

// built-in middleware to handle urlencoded form data
app.use(express.urlencoded({ extended: false }));

// built-in middleware for json 
app.use(express.json());

//middleware for cookies
app.use(cookieParser());

//serve static files
app.use('/', express.static(path.join(__dirname, '/public')));

// routes
app.use('/', require('./routes/root'));
app.use('/register', require('./routes/register'));
app.use('/auth', require('./routes/auth'));
app.use('/refresh', require('./routes/refresh'));
app.use('/logout', require('./routes/logout'));

app.use(verifyJWT);
app.use('/employees', require('./routes/api/employees'));

app.all('*', (req, res) => {
    res.status(404);
    if (req.accepts('html')) {
        res.sendFile(path.join(__dirname, 'views', '404.html'));
    } else if (req.accepts('json')) {
        res.json({ "error": "404 Not Found" });
    } else {
        res.type('txt').send("404 Not Found");
    }
});

app.use(errorHandler);

app.listen(PORT, () => console.log(`Server running on port ${PORT}`));

auth.js

const express = require('express');
const router = express.Router();
const authController = require('../controllers/authController');

router.post('/', authController.handleLogin);

module.exports = router;

authController.js

const usersDB = {
    users: require('../model/users.json'),
    setUsers: function (data) {
        this.users = data;
    },
};
const bcrypt = require('bcrypt');

const jwt = require('jsonwebtoken');
require('dotenv').config();
const fsPromises = require('fs').promises;
const path = require('path');

const handleLogin = async (req, res) => {
    const { user, pwd } = req.body;
    if (!user || !pwd)
        return res
            .status(400)
            .json({ message: 'Username and password are required.' });
    const foundUser = usersDB.users.find((person) => person.username === user);
    if (!foundUser) return res.sendStatus(401); //Unauthorized
    // evaluate password
    const match = await bcrypt.compare(pwd, foundUser.password);
    if (match) {
        const roles = Object.values(foundUser.roles).filter(Boolean);
        // create JWTs
        const accessToken = jwt.sign(
            {
                UserInfo: {
                    "username": foundUser.username,
                    roles: roles,
                },
            },
            process.env.ACCESS_TOKEN_SECRET,
            { expiresIn: '30s' }
        );
        const refreshToken = jwt.sign(
            { "username": foundUser.username },
            process.env.REFRESH_TOKEN_SECRET,
            { expiresIn: '1d' }
        );

        // Saving refreshToken with current user
        const otherUsers = usersDB.users.filter(
            (person) => person.username !== foundUser.username
        );
        const currentUser = { ...foundUser, refreshToken };
        usersDB.setUsers([...otherUsers, currentUser]);
        await fsPromises.writeFile(
            path.join(__dirname, '..', 'model', 'users.json'),
            JSON.stringify(usersDB.users)
        );
        res.cookie('jwt', refreshToken, {
            httpOnly: true,
            sameSite: 'None',
            secure: true,
            maxAge: 24 * 60 * 60 * 1000,
        });
        res.json({ accessToken });
    } else {
        res.sendStatus(401);
    }
};

module.exports = { handleLogin };

排查方向与解决方案

1. 文件系统写入权限问题(核心原因)

Render平台的文件系统是临时且只读的(除指定持久化目录外),你的代码在authController.js中尝试写入users.json文件:

await fsPromises.writeFile(
    path.join(__dirname, '..', 'model', 'users.json'),
    JSON.stringify(usersDB.users)
);

这会导致新注册用户的信息无法持久化,登录时从users.json中找不到新用户,直接返回401。

解决方法:

  • 替换为正式数据库(如PostgreSQL、MongoDB),Render官方支持这些数据库服务;
  • 若仅临时测试,可配置Render的持久存储卷挂载到model目录,但该方案不适合生产环境。

2. Cookie属性配置验证

线上环境中secure: true要求请求必须为HTTPS,如果前端请求使用HTTP(或本地测试时的HTTP),Cookie无法被正确设置。不过你本地正常,线上新用户登录401更可能是文件系统问题,但可临时注释secure: true验证。

3. 环境变量一致性检查

确认Render平台的环境变量名称与代码中完全一致:

  • 检查ACCESS_TOKEN_SECRET和REFRESH_TOKEN_SECRET的拼写、大小写是否匹配;
  • 可临时添加日志console.log(process.env.ACCESS_TOKEN_SECRET)验证变量是否加载成功(注意不要泄露密钥)。

4. 用户数据存在性验证

在Render平台通过控制台或日志查看users.json内容,确认新注册用户是否被写入文件。


内容的提问来源于stack exchange,提问作者Krystian Owczarek

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 07:42:49