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

ExpressJS/Passport JWT授权异常:localhost正常127.0.0.1失效

授权问题排查:localhost正常,127.0.0.1失效

问题背景

前端在localhost域名下用户授权功能正常,但切换到127.0.0.1时授权直接失效。前端部署于Vercel,后端部署于render.com。

已尝试的解决方法

  • 修改CORS配置源(无效)
  • 在请求中设置credentials: 'include'、same-origin、no-cors等选项(无效)
  • 尝试使用ExtractJwt.fromAuthHeaderWithScheme('Bearer')提取JWT Token(无效)

依赖版本

{
  "cookie-parser": "^1.4.6",
  "cors": "^2.8.5",
  "express": "^4.19.2",
  "jsonwebtoken": "^9.0.2",
  "passport": "^0.7.0",
  "passport-jwt": "^4.0.1",
  "passport-local": "^1.0.0"
}

前端代码

api.js

const API_HOST = 'http://localhost:3000';

class ApiService {
    async request(path, method = 'GET', body = null) {
        console.log('Request', { path, method, body });

        const url = [API_HOST, path].join(path[0] === '/' ? '' : '/');
        const isJson = body && body instanceof Object ? JSON.stringify(body) : body;
        const headers = new Headers();

        if (isJson) {
            headers.set('Content-Type', 'application/json');
        }

        const res = await fetch(url, {
            method: method,
            credentials: 'include',
            headers: headers,
            body: isJson ? JSON.stringify(body) : body
        });

        let data = await res.text();

        try {
            data = JSON.parse(data);
        } catch (err) {}

        if (res.status < 200 || res.status >= 300) {
            throw new Error(data instanceof Object ? data.err : data);
        }

        return data;
    }
}

class UserApiService extends ApiService {
    login(username, password) {
        return this.request('/login', 'POST', { username, password });
    }

    logout() {
        return this.request('/logout', 'POST');
    }

    me() {
        return this.request('/me', 'GET');
    }
}

login.html

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login Page</title>
    <script src="./api.js"></script>
</head>
<body>
    <form action="#" id="loginForm">
        <input type="text" name="username">
        <input type="password" name="password">
        <button type="submit">Login</button>
    </form>

    <script>
        const userApi = new UserApiService();

        const formElement = document.getElementById('loginForm');

        formElement.addEventListener('submit', async (event) => {
            event.preventDefault();

            const username = event.target.username.value;
            const password = event.target.password.value;

            const user = await userApi.login(username, password);

            location.pathname = '/admin.html';
        });
    </script>
</body>
</html>

admin.html

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Admin Page</title>
    <script src="./api.js"></script>
</head>
<body>
    <h1 id="title"></h1>

    <button id="logoutButton">Logout</button>

    <script>
        const userApi = new UserApiService();

        document.getElementById('logoutButton').addEventListener('click', async () => {
            await userApi.logout();
            location.pathname = '/login.html';
        });

        userApi.me()
            .then(user => {
                const titleElement = document.getElementById('title');
                titleElement.innerText = `Welcome, ${user.username}!`
            })
            .catch(err => {
                location.pathname = '/login.html';
                throw err;
            });
    </script>
</body>
</html>

后端代码

index.js

const express = require('express');
const cors = require('cors');
const cookieParser = require('cookie-parser');
const jwt = require('jsonwebtoken');
const passport = require('passport');
const { Strategy: LocalStrategy } = require('passport-local');
const { Strategy: JwtStrategy, ExtractJwt } = require('passport-jwt');

const config = require('./config');

//! Replace 'config.devTest' parts with real logic

/* ----------------[ Custom Errors ] ----------------*/

class APIError extends Error {
  constructor(status, message = '') {
    super(message);
    this.status = status;
  }
}

/* ----------------[ Instances and Constants ] ----------------*/

const app = express();
const PORT = process.env.PORT || 3000;

const requireAuth = passport.authenticate('jwt', { session: false });

/* ----------------[ Passport Initialization ] ----------------*/

passport.use(
  'local',
  new LocalStrategy(
    (username, password, done) => {
      const { user } = config.devTest;

      if (
        username !== user.username ||
        password !== user.password
      ) {
        done(new APIError(401, 'Invalid username or password'));
      }

      // Generates new JWT token
      const token = jwt.sign({ id: user.id }, config.jwtSecret, { expiresIn: '1h' });

      done(null, token);
    }
  )
);

passport.use(
  'jwt',
  new JwtStrategy(
    {
      secretOrKey: config.jwtSecret,
      jwtFromRequest: ExtractJwt.fromExtractors([
        // Extract JWT from auth header
        ExtractJwt.fromAuthHeaderAsBearerToken(),
        // Extract JWT from cookies
        (req) => req.cookies[config.cookieSessionKey]
      ]),
    },
    (payload, done) => {
      const { id: userId } = payload;

      if (userId && userId === config.devTest.user.id) {
        return done(null, config.devTest.user);
      } else {
        return done(new APIError(401, 'Validation Failed'), false);
      }
    }
  )
);

// Used to serialize user (from object to id)
passport.serializeUser((user, done) => {
  done(null, user.id)
});

// Used to deserialize user (from id to object)
passport.deserializeUser((id, done) => {
  const user = { id, username: 'User' }; // Replace this part with real user find logic

  done(null, user);
});

/* ----------------[ App Initialization ] ----------------*/

app.use(cors({
  // Similar to the '*' wildcard, but bypasses cors restrictions
  origin: (origin, callback) => callback(null, true),
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
}));

app.use(cookieParser());
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

app.use(passport.initialize());

/* ----------------[ App Routes ] ----------------*/

app.post('/login', passport.authenticate('local', { session: false }), async (req, res) => {
  const token = req.user;

  // Saves and sends JWT token to the user
  res
    .cookie(config.cookieSessionKey, token, { httpOnly: true, secure: false })
    .json({ token });
});

app.post('/logout', requireAuth, async (req, res) => {
  res
    .clearCookie(config.cookieSessionKey)
    .end();
});

app.get('/me', requireAuth, async (req, res) => {
  res.json(req.user);
});

// Global error handler
app.use((err, req, res, next) => {
  if (err instanceof APIError) {
    res.status(err.status).json({ err: err.message });
    return;
  }

  res.sendStatus(500);
});

app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 22:55:55