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

基于Node.js+Express,如何替换express-session改用客户端存储JWT令牌?

Got it, let's walk through how to transition from express-session's cookie-based auth to JWT tokens while keeping your existing Passport local setup and MongoDB stack. I've broken this down into actionable steps tailored to your tech stack:

1. Install Required Dependencies

First, add the packages needed to handle JWT creation, validation, and Passport integration:

npm install jsonwebtoken passport-jwt

You should already have passport-local, mongoose, and bcryptjs (for password hashing) from your existing setup, but double-check they're installed if not.

2. Update Your User Model (MongoDB/Mongoose)

Ensure your user schema has password hashing logic (if it doesn't already) and a method to compare passwords. Here's a standard example:

const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');

const userSchema = new mongoose.Schema({
  email: { type: String, required: true, unique: true },
  password: { type: String, required: true }
});

// Hash password before saving to DB
userSchema.pre('save', async function(next) {
  if (!this.isModified('password')) return next();
  this.password = await bcrypt.hash(this.password, 12);
  next();
});

// Method to compare candidate password with stored hash
userSchema.methods.comparePassword = async function(candidatePassword) {
  return await bcrypt.compare(candidatePassword, this.password);
};

module.exports = mongoose.model('User', userSchema);
3. Configure Passport JWT Strategy

Create a Passport config file (e.g., config/passport.js) to set up JWT validation. This tells Passport how to extract tokens from requests and verify them against your MongoDB users:

const passport = require('passport');
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const User = require('../models/User');

const opts = {
  // Extract token from the "Authorization: Bearer <token>" header
  jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
  // Use an environment variable for the secret (never hardcode this!)
  secretOrKey: process.env.JWT_SECRET
};

passport.use(new JwtStrategy(opts, async (jwtPayload, done) => {
  try {
    const user = await User.findById(jwtPayload.id);
    if (user) {
      return done(null, user); // Attach user to req.user for protected routes
    }
    return done(null, false); // No user found
  } catch (err) {
    return done(err, false);
  }
}));

module.exports = passport;
4. Rewrite Auth Routes for JWT

Replace your existing session-based login route with one that issues a JWT after successful authentication. You'll also add a protected route example to test the JWT validation:

const express = require('express');
const router = express.Router();
const jwt = require('jsonwebtoken');
const User = require('../models/User');

// Login route: Verify credentials and return JWT
router.post('/login', async (req, res) => {
  try {
    const { email, password } = req.body;
    const user = await User.findOne({ email });

    if (!user) {
      return res.status(401).json({ message: 'Invalid credentials' });
    }

    const isPasswordMatch = await user.comparePassword(password);
    if (!isPasswordMatch) {
      return res.status(401).json({ message: 'Invalid credentials' });
    }

    // Generate JWT (expires in 1 hour - adjust as needed)
    const token = jwt.sign(
      { id: user._id, email: user.email },
      process.env.JWT_SECRET,
      { expiresIn: '1h' }
    );

    res.json({ token }); // Send token to client
  } catch (err) {
    res.status(500).json({ message: 'Server error' });
  }
});

// Protected route: Requires valid JWT to access
router.get('/profile', require('../config/passport').authenticate('jwt', { session: false }), (req, res) => {
  // req.user contains the authenticated user object
  res.json({
    id: req.user._id,
    email: req.user.email
  });
});

module.exports = router;
5. Clean Up express-session

Since you're moving away from session cookies, remove the express-session middleware and passport.session() from your main app file. Keep only passport.initialize():

const express = require('express');
const passport = require('./config/passport');
const authRoutes = require('./routes/auth');

const app = express();

// Middleware
app.use(express.json());
app.use(passport.initialize()); // Remove passport.session() entirely

// Routes
app.use('/api/auth', authRoutes);

// Start server
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
6. Client-Side JWT Handling

On the client, store the JWT after login (usually in localStorage or sessionStorage) and include it in the Authorization header for every protected request:

// Login function
async function login(email, password) {
  const response = await fetch('/api/auth/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email, password })
  });
  const data = await response.json();
  if (response.ok) {
    localStorage.setItem('jwtToken', data.token);
  } else {
    alert(data.message);
  }
}

// Request protected profile data
async function fetchProfile() {
  const token = localStorage.getItem('jwtToken');
  const response = await fetch('/api/auth/profile', {
    headers: { 'Authorization': `Bearer ${token}` }
  });
  const profile = await response.json();
  console.log(profile);
}
Critical Security Reminders
  • Always use HTTPS: JWTs are plaintext in transit, so HTTPS prevents interception.
  • Secure your JWT secret: Store it in an environment variable (never commit it to version control).
  • Limit token expiration: Short-lived tokens reduce risk if stolen. Consider adding a refresh token flow for longer sessions.
  • Mitigate XSS risks: Storing JWTs in localStorage exposes them to XSS attacks. Ensure your frontend has strong XSS protections, or use HttpOnly cookies for refresh tokens if you implement that flow.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:40:12