# JWT 鉴权

URL: https://caijiao.org/koa/practice/jwt
Source: docs/koa/practice/jwt.md
Description: JWT（JSON Web Token）是实现 Web 应用程序安全认证的一种常用方式，广泛应用于现代 Web 系统中。本节将介绍如何在 Koa 中实现基于 JWT 的身份认证和授权。

JWT（JSON Web Token）是一种基于 Token 的认证方式，通常用于客户端和服务器之间传递信息，常用于 Web 应用的用户身份认证。它是一种自包含的、安全的认证方法，广泛用于 API 认证、单点登录（SSO）等场景。

## 为什么使用 JWT？

- **无状态认证**：JWT 是自包含的，可以存储用户的身份信息和权限，不需要在服务器存储会话信息。
- **跨平台支持**：JWT 作为一种标准的认证机制，支持多平台、多语言实现。
- **安全性**：JWT 通过签名和加密保护数据的完整性和机密性。
- **方便扩展**：JWT 可以灵活地扩展其他自定义数据，如用户权限、角色等。

## JWT 的结构

JWT 由三个部分组成：

1. **头部（Header）**：通常包括类型（JWT）和使用的签名算法（如 HMAC SHA256 或 RSA）。
2. **有效载荷（Payload）**：包含用户信息以及一些元数据。有效载荷是可以解码的，但不加密，因此不应存储敏感数据。
3. **签名（Signature）**：用来验证消息是否被篡改，使用头部中的算法和密钥对头部和有效载荷进行签名。

JWT 的结构格式如下：

```

Header.Payload.Signature

````

## 在 Koa 中实现 JWT 鉴权

### 安装相关依赖

首先，我们需要安装 JWT 相关的依赖库 `jsonwebtoken`：

```bash
npm install jsonwebtoken
````

### 生成 JWT Token

在用户登录时，我们会生成一个 JWT Token，并将其返回给客户端。客户端可以在后续请求中携带该 Token。

```js
// auth.js
const jwt = require('jsonwebtoken');

const SECRET_KEY = 'your-secret-key'; // 替换为强随机密钥

// 登录时生成 JWT Token
const generateToken = (user) => {
  const payload = {
    id: user.id,
    username: user.username,
  };

  // 设置 Token 的有效期
  const options = { expiresIn: '1h' };

  // 使用密钥和算法生成 Token
  return jwt.sign(payload, SECRET_KEY, options);
};

module.exports = { generateToken };
```

### 校验 JWT Token

在后续的请求中，客户端需要将 Token 放在请求头中。我们通过中间件来验证 Token 是否有效。

```js
// authMiddleware.js
const jwt = require('jsonwebtoken');
const SECRET_KEY = 'your-secret-key'; // 替换为强随机密钥

// 校验 JWT Token 中间件
const verifyToken = (ctx, next) => {
  const token = ctx.headers['authorization'];

  if (!token) {
    ctx.status = 401;
    ctx.body = { message: 'Token is required' };
    return;
  }

  try {
    // 校验 Token 是否有效
    const decoded = jwt.verify(token, SECRET_KEY);

    // 将用户信息存储在 ctx.state 中，供后续使用
    ctx.state.user = decoded;
    return next();
  } catch (error) {
    ctx.status = 401;
    ctx.body = { message: 'Invalid or expired token' };
  }
};

module.exports = { verifyToken };
```

### 保护路由

使用 JWT 校验中间件来保护需要身份认证的路由。例如，只有经过认证的用户才能访问某些 API 接口。

```js
// user.js
const Router = require('koa-router');
const { verifyToken } = require('./authMiddleware');
const router = new Router();

// 需要身份认证的路由
router.get('/profile', verifyToken, async (ctx) => {
  const user = ctx.state.user; // 获取用户信息

  ctx.body = { message: 'Profile data', user };
});

module.exports = router;
```

### 完整示例

以下是一个完整的 Koa JWT 鉴权示例，包含生成 Token、校验 Token 和保护路由的步骤。

```js
const Koa = require('koa');
const Router = require('koa-router');
const jwt = require('jsonwebtoken');
const { generateToken } = require('./auth');
const { verifyToken } = require('./authMiddleware');

const app = new Koa();
const router = new Router();

// 用户登录，生成 JWT Token
router.post('/login', async (ctx) => {
  const { username, password } = ctx.request.body;

  // 简单的用户验证逻辑
  if (username === 'user' && password === 'password') {
    const user = { id: 1, username };
    const token = generateToken(user);
    ctx.body = { token };
  } else {
    ctx.status = 401;
    ctx.body = { message: 'Invalid username or password' };
  }
});

// 需要身份认证的保护路由
router.get('/profile', verifyToken, async (ctx) => {
  const user = ctx.state.user; // 获取经过验证的用户信息
  ctx.body = { message: 'Profile data', user };
});

app.use(router.routes()).use(router.allowedMethods());
app.listen(3000, () => {
  console.log('Server running on port 3000');
});
```

## 总结

通过上述步骤，我们在 Koa 中实现了基于 JWT 的身份认证和授权。JWT 是一种轻量级的认证机制，适用于无状态的应用，能够有效地进行用户认证和授权。通过在 [Koa](/koa/) 中实现生成 Token、验证 Token 和保护路由，我们能够为 Web 应用提供安全的身份验证功能。
