还只会express?今儿使用Koa2 实现 Jwt鉴权

Jwt认识

Jwt (JSON Web Token),是客户端和服务端进行通信 ,服务器向用户发送的 JSON对象。

因为http是一种无状态协议。每次连接完成后,两端(客户端和服务端)完成资源传输后关闭,再次交互时重新建立新的连接。服务端无法跟踪上次用户访问的相同资源。

于是又token 或是 cookie 认证,极大增强交互和安全性

这样使用cookie 认证流程:

  • 这时候使用cookie(未到期)一种文本形式,在浏览器端存储(内存或硬盘)。如果用户访问过,服务端会根据浏览器带上的cookie 凭据进行 验证【cookie 实现一般通过定义http请求头或 set-cookie 的响应头 】
  • 用户向服务器发送用户名和密码等信息
  • 服务器验证通过后,在当前对话(session)保存相关数据,比如:用户角色、登入信息等
  • 用户第一次访问,服务器会记录这次的对话sessionId,并将这个记录通过cookie返回给浏览器
  • 用户下次访问时,浏览器带上cookie 和sessionId进行比配,是否正确
  • 注意:cookie 一般存在浏览器。而session 相对安全得 存在服务器,如果过期,服务器有自动回收session 会话机制

JWT 的数据结构

JWT 的三个部分依次如下:

  • Header(头部),类似如下
json 复制代码
{
  "alg": "HS256",
  "typ": "JWT"
}

alg 属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256)。typ 属性表示这个令牌(token)的类型(type),JWT 令牌统一写为 JWT

  • Payload(负载)。也是一个 JSON,用来存放实际需要传递的数据。JWT 规定了 7 个官方字段。如下所示
  • iss (issuer):签发人
  • exp (expiration time):过期时间
  • sub (subject):主题
  • aud (audience):受众
  • nbf (Not Before):生效时间
  • iat (Issued At):签发时间
  • jti (JWT ID):编号

当然也可以自定义私有字段。但是要注意,JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。

  • Signature(签名)。Signature 部分是对前两部分的签名,防止数据篡改。首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。
scss 复制代码
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

算出签名以后,把 HeaderPayloadSignature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户

现在使用Koa2 进行搭建

我们要用 Koa2 搭一个简单的用户系统,关键功能就两个:

  1. 用户登录,服务端给个 token,客户端拿着。
  2. 下次请求,客户端把 token 交给服务端,服务端验一下,没问题就给数据,不行就报错(401)。
  • 用到的库:
    • jsonwebtoken:生成和校验 token 的主力。
    • koa-jwt:把 jsonwebtoken 封装一下,方便在 Koa2 里用,主要是用来校验 token。
    • axios:前端发请求用的,顺便用它的拦截器处理请求数据。

三、项目结构

bash 复制代码
koa-jwt-demo/
├── package.json
├── app.js
├── controllers/
│   ├── user.js
│   └── home.js
├── middlewares/
│   └── error.js
├── routes/
│   └── index.js
└── utils/
    └── jwt.js

四、动手搭建

在项目根目录下运行以下命令安装必要的依赖:

bash 复制代码
npm install koa koa-router koa-bodyparser koa-jwt jsonwebtoken bcryptjs

1. 初始化项目

先搭个架子,跑起来再说。

Koa 本身不解析请求体,但可以通过 koa-bodykoa-bodyparser 中间件实现。

app.js

JavaScript 复制代码
// app.js
const Koa = require('koa');
const Router = require('koa-router');
const bodyParser = require('koa-bodyparser');
const jwt = require('koa-jwt');
const { secret } = require('./utils/jwt'); // JWT 密钥
const errorMiddleware = require('./middlewares/error'); // 错误处理中间件
const routes = require('./routes'); // 路由配置

const app = new Koa();

// 错误处理中间件
app.use(errorMiddleware);

// 解析请求体
app.use(bodyParser());

// JWT 鉴权中间件
app.use(jwt({ secret }).unless({ path: [/^\/api\/auth/]})); // 除了 /api/auth 路径外,其他路径都需要 JWT 鉴权

// 挂载路由
app.use(routes.routes());
app.use(routes.allowedMethods());

app.listen(3000, () => {
    console.log('Server is running on port 3000');
});

2. 错误处理中间件

出错别慌,这里兜底。

下面使用Koa 中间件next,用于处理请求和响应。中间件可以使用async / await,执行顺序遵循"洋葱模型",即先递归到最内层,再逐层回溯

middlewares/error.js

JavaScript 复制代码
module.exports = async (ctx, next) => {
    try {
        await next();
    } catch (err) {
        ctx.status = err.status || 500;
        ctx.body = { message: err.message };
    }
};

3. JWT 工具函数

生成 token,校验 token,全靠它。

utils/jwt.js

JavaScript 复制代码
// utils/jwt.js
const jwt = require('jsonwebtoken');

const secret = 'your_jwt_secret'; // JWT 密钥

// 生成 JWT 令牌
const generateToken = (user) => {
    return jwt.sign(user, secret, { expiresIn: '1h' }); // 令牌有效期为 1 小时
};

module.exports = { secret, generateToken };

4. 用户控制器

用户注册、登录,搞定。

controllers/user.js

JavaScript 复制代码
// controllers/user.js
const bcrypt = require('bcryptjs');
const { generateToken } = require('../utils/jwt');

const users = []; // 模拟用户存储

// 用户注册
const register = async (ctx) => {
    const { username, password } = ctx.request.body;
    const hashedPassword = await bcrypt.hash(password, 10); // 加密密码
    users.push({ username, password: hashedPassword });
    ctx.body = { message: 'User registered successfully' };
};

// 用户登录
const login = async (ctx) => {
    const { username, password } = ctx.request.body;
    const user = users.find(u => u.username === username);
    if (!user || !(await bcrypt.compare(password, user.password))) {
        ctx.status = 401;
        ctx.body = { message: 'Invalid credentials' };
        return;
    }
    const token = generateToken({ username }); // 生成 JWT 令牌
    ctx.body = { token };
};

module.exports = { register, login };

5. 主页控制器

受保护的资源,拿 token 才能进,koa可以使用ctx 设置body,satus等响应内容

controllers/home.js

JavaScript 复制代码
const home = (ctx) => {
    ctx.body = { message: 'Welcome to the protected area' };
};

module.exports = { home };

6. 路由配置

Koa 本身不内置路由功能,但可以通过 koa-router 插件实现

routes/index.js

JavaScript 复制代码
// routes/index.js
const Router = require('koa-router');
const { register, login } = require('../controllers/user');
const { home } = require('../controllers/home');

const router = new Router({ prefix: '/api' });

// 用户注册
router.post('/auth/register', register);

// 用户登录
router.post('/auth/login', login);

// 受保护的资源
router.get('/home', home);

module.exports = router;

五、前端请求拦截器

前端用 axios,每次请求带上 token。

JavaScript 复制代码
import axios from 'axios';

axios.interceptors.request.use(config => {
    const token = localStorage.getItem('token');
    config.headers.common['Authorization'] = 'Bearer ' + token;
    return config;
});

六、跑起来试试

  1. 启动服务:node app.js

  2. 测试:

    • 注册:POST http://localhost:3000/api/auth/register,带个 { "username": "testuser", "password": "password" }
    • 登录:POST http://localhost:3000/api/auth/login,用刚才的账号密码。
    • 拿到 token,存到 localStorage 里。
    • 访问:GET http://localhost:3000/api/home,看能不能拿到欢迎信息。

七、总结

这个项目就是个"麻雀虽小,五脏俱全"的例子,把 Koa2、JWT 鉴权、错误处理这些关键点都串起来了。JWT 的流程也很清晰:登录拿 token,请求带 token,服务端验 token,搞定!

希望这个实战例子能让你快速上手,要是遇到啥问题,直接问,别客气!

相关推荐
Li_Ning2122 分钟前
【Docker】让前端也能用Docker部署,以ruoyi(若依)为例,极简部署流程
前端·docker·容器
zhenryx4 小时前
前端-导出png,jpg,pptx,svg
开发语言·前端·javascript
QBorfy4 小时前
02篇 AI从零开始 - 部署本地大模型 DeepSeek-R1
前端·人工智能·deepseek
QBorfy4 小时前
01篇 AI从零开始 - 基础知识和环境准备
前端·人工智能
115432031q4 小时前
基于SpringBoot养老院平台系统功能实现十五
java·前端·后端
朦胧之5 小时前
React Native 新架构 (一)
前端·react native
患得患失9495 小时前
【前端】【面试】ref与reactive的区别
前端·面试·vue3
牛奶5 小时前
前端视界:图解React
前端·react.js·面试
三原6 小时前
Vue Playground 演练场源码解读(二)
前端·vue.js·源码