教程 express tutorial

使用 JWT 验证

JSON Web Token (JWT) 是一种开放标准(RFC 7519),用于在网络应用环境中以一种简短、安全的方式传递信息。JWT 在实现用户认证时比传统的基于 Session 的验证方式更加灵活,尤其适用于分布式架构、微服务等场景。在本章节中,我们将展示如何在 Express 中使用 JWT 进行用户认证。

1. 安装依赖

首先,我们需要安装 jsonwebtoken 库来生成和验证 JWT:

bash
npm install jsonwebtoken

`

我们还将使用 bcryptjs 来加密用户密码,提高系统的安全性:

bash
npm install bcryptjs

2. 配置 Express 项目

接着,创建一个基本的 Express 应用,并设置必要的中间件:

js
const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const bodyParser = require('body-parser');

const app = express();
const port = 3000;
const SECRET_KEY = 'your-secret-key';  // 用于加密和解密 JWT 的密钥

// 使用 body-parser 解析 POST 请求的表单数据
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

// 用户数据库模拟
const users = [
  { username: 'john_doe', password: bcrypt.hashSync('password123', 10) },
  { username: 'jane_doe', password: bcrypt.hashSync('12345', 10) }
];

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

在上面的代码中,bcrypt.hashSync 用于加密密码,这样即使用户密码暴露,数据也更为安全。

3. 用户注册与登录

3.1 用户注册

用户注册时,我们将密码通过 bcryptjs 进行加密,并将用户信息存储在我们的“数据库”中。

js
app.post('/register', (req, res) => {
  const { username, password } = req.body;

  // 检查用户名是否已经存在
  const userExists = users.some(user => user.username === username);
  if (userExists) {
    return res.status(400).send('用户名已存在');
  }

  // 加密用户密码
  const hashedPassword = bcrypt.hashSync(password, 10);

  // 将新用户添加到 "数据库" 中
  users.push({ username, password: hashedPassword });
  res.send('注册成功');
});

3.2 用户登录

在用户登录时,我们首先验证用户名和密码,如果正确,我们将为用户生成一个 JWT 并返回给客户端,客户端可以在后续请求中通过 HTTP 请求头携带这个 JWT 进行身份验证。

js
app.post('/login', (req, res) => {
  const { username, password } = req.body;

  // 查找匹配的用户
  const user = users.find(u => u.username === username);
  if (!user) {
    return res.status(401).send('用户名或密码错误');
  }

  // 验证密码是否匹配
  const isPasswordValid = bcrypt.compareSync(password, user.password);
  if (!isPasswordValid) {
    return res.status(401).send('用户名或密码错误');
  }

  // 生成 JWT
  const token = jwt.sign({ username: user.username }, SECRET_KEY, { expiresIn: '1h' });

  res.json({ token });
});

在这段代码中,我们通过 jwt.sign() 生成一个包含用户名的 JWT,并设置了过期时间为 1 小时。生成的 JWT 将被返回给客户端。

4. 保护需要认证的路由

在需要用户认证的路由上,我们需要验证客户端传递的 JWT 是否有效。我们可以通过 Authorization 请求头获取 JWT,然后使用 jsonwebtoken 库进行解密和验证。

4.1 创建认证中间件

js
function authenticateToken(req, res, next) {
  const token = req.header('Authorization') && req.header('Authorization').split(' ')[1];  // 获取请求头中的 token

  if (!token) {
    return res.status(403).send('访问被拒绝,未提供 token');
  }

  // 验证 token
  jwt.verify(token, SECRET_KEY, (err, user) => {
    if (err) {
      return res.status(403).send('无效的 token');
    }
    req.user = user;  // 将解码后的用户信息附加到请求对象上
    next();  // 继续执行后续的路由处理
  });
}

在这个中间件中,我们首先检查请求头中是否包含 Authorization 字段,如果存在则获取并验证 JWT。如果验证成功,解码后的用户信息将被存储在 req.user 中,继续执行后续的路由处理;如果验证失败,则返回 403 错误。

4.2 保护路由

现在,我们可以保护需要认证的路由。只有在用户提供有效的 JWT 时,才能访问这些路由。

js
app.get('/dashboard', authenticateToken, (req, res) => {
  res.send(`欢迎回来,${req.user.username}`);
});

在上面的代码中,/dashboard 路由需要用户认证才能访问。如果请求中没有有效的 JWT,authenticateToken 中间件将返回 403 错误。

5. 退出登录

使用 JWT 时,用户退出登录的操作相对简单。JWT 不会存储在服务器端,因此只需要客户端删除存储的 JWT 即可实现退出。

js
app.get('/logout', (req, res) => {
  res.send('已退出登录');
});

客户端只需要删除浏览器中存储的 JWT(例如,在 localStorage 或 sessionStorage 中),即可实现登出。

6. 完整代码示例

以下是完整的 Express 应用代码,展示了如何使用 JWT 来实现用户认证、保护路由以及退出登录的功能:

js
const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const bodyParser = require('body-parser');

const app = express();
const port = 3000;
const SECRET_KEY = 'your-secret-key';

// 使用 body-parser 解析 POST 请求的表单数据
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

// 用户数据库模拟
const users = [
  { username: 'john_doe', password: bcrypt.hashSync('password123', 10) },
  { username: 'jane_doe', password: bcrypt.hashSync('12345', 10) }
];

// 用户注册路由
app.post('/register', (req, res) => {
  const { username, password } = req.body;
  const userExists = users.some(user => user.username === username);
  if (userExists) {
    return res.status(400).send('用户名已存在');
  }
  const hashedPassword = bcrypt.hashSync(password, 10);
  users.push({ username, password: hashedPassword });
  res.send('注册成功');
});

// 用户登录路由
app.post('/login', (req, res) => {
  const { username, password } = req.body;
  const user = users.find(u => u.username === username);
  if (!user) {
    return res.status(401).send('用户名或密码错误');
  }
  const isPasswordValid = bcrypt.compareSync(password, user.password);
  if (!isPasswordValid) {
    return res.status(401).send('用户名或密码错误');
  }
  const token = jwt.sign({ username: user.username }, SECRET_KEY, { expiresIn: '1h' });
  res.json({ token });
});

// 认证中间件
function authenticateToken(req, res, next) {
  const token = req.header('Authorization') && req.header('Authorization').split(' ')[1];
  if (!token) {
    return res.status(403).send('访问被拒绝,未提供 token');
  }
  jwt.verify(token, SECRET_KEY, (err, user) => {
    if (err) {
      return res.status(403).send('无效的 token');
    }
    req.user = user;
    next();
  });
}

// 保护路由
app.get('/dashboard', authenticateToken, (req, res) => {
  res.send(`欢迎回来,${req.user.username}`);
});

// 退出登录
app.get('/logout', (req, res) => {
  res.send('已退出登录');
});

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

总结

本章节介绍了如何在 Express 中使用 JWT 实现用户认证:

  1. 用户注册与登录:通过 jsonwebtoken 库生成和验证 JWT,客户端存储 JWT 以便后续请求使用。
  2. 保护路由:使用中间件验证 JWT,确保只有持有有效 JWT 的用户才能访问受保护的路由。
  3. 退出登录:JWT 不存储在服务器端,客户端