Express中req.user在Elysia JS中的等价实现方案问询
在Elysia JS中实现类似Express req.user的请求级用户信息存储
核心思路
Elysia没有直接对应Express req.user的API,但可以通过请求上下文存储(ctx.store)或扩展上下文属性来实现请求生命周期内的用户信息共享,完全满足你的需求。
先回顾你的Express实现(示例)
const jwt = require('jsonwebtoken'); // JWT认证中间件 const authenticateJWT = (req, res, next) => { const authHeader = req.headers.authorization; if (authHeader) { const token = authHeader.split(' ')[1]; jwt.verify(token, process.env.JWT_SECRET, (err, user) => { if (err) return res.sendStatus(403); req.user = user; // 存入req.user供后续复用 next(); }); } else { res.sendStatus(401); } }; // 权限校验中间件 const requireAdmin = (req, res, next) => { if (req.user?.role === 'admin') next(); else res.sendStatus(403); };
Elysia 实现方案
方案1:使用内置ctx.store存储用户信息(推荐,无需额外配置)
ctx.store是Elysia内置的请求级临时存储,每个请求独立,专门用于在同一请求的钩子、路由之间共享数据,完美替代req.user。
结合Elysia官方JWT插件实现:
import { Elysia } from 'elysia'; import jwt from '@elysiajs/jwt'; const app = new Elysia(); // 注册官方JWT插件 app.use(jwt({ secret: process.env.JWT_SECRET })); // 自定义认证钩子:解析JWT并存储用户信息到ctx.store.user app.hook('beforeHandle', async (ctx) => { const authHeader = ctx.headers.authorization; if (!authHeader) { ctx.set.status = 401; return 'Unauthorized'; } const token = authHeader.split(' ')[1]; try { // 验证JWT并解析用户信息 const user = await ctx.jwt.verify(token); // 存入请求级存储 ctx.store.user = user; } catch (err) { ctx.set.status = 403; return 'Forbidden: Invalid token'; } }); // 权限校验钩子:复用存储的用户信息 app.hook('beforeHandle', (ctx) => { // 仅对需要admin权限的路由启用,可通过路由分组或插件控制 const user = ctx.store.user; if (!user || user.role !== 'admin') { ctx.set.status = 403; return 'Forbidden: Admin required'; } }); // 示例路由:直接复用用户信息 app.get('/protected', (ctx) => { return { message: `Hello, ${ctx.store.user.username}`, userInfo: ctx.store.user }; }); app.listen(3000);
方案2:扩展上下文属性(TypeScript友好)
如果使用TypeScript,你可以直接扩展Elysia的上下文类型,添加ctx.user属性,获得更好的类型提示:
import { Elysia } from 'elysia'; import jwt from '@elysiajs/jwt'; // 扩展Elysia上下文类型 declare module 'elysia' { interface Context { user: { id: string; username: string; role: string; }; } } const app = new Elysia(); app.use(jwt({ secret: process.env.JWT_SECRET })) .hook('beforeHandle', async (ctx) => { const authHeader = ctx.headers.authorization; if (!authHeader) { ctx.set.status = 401; return 'Unauthorized'; } const token = authHeader.split(' ')[1]; try { ctx.user = await ctx.jwt.verify(token); // 直接赋值给ctx.user } catch (err) { ctx.set.status = 403; return 'Forbidden: Invalid token'; } }) .get('/admin', (ctx) => { return { message: `Welcome admin ${ctx.user.username}` }; }); app.listen(3000);
进阶:封装成可复用插件
按照Elysia的插件化设计,可以把认证和权限校验封装成独立插件,方便在多个路由或应用中复用:
// auth.plugin.js import { Elysia } from 'elysia'; import jwt from '@elysiajs/jwt'; // 认证插件 export const authPlugin = new Elysia() .use(jwt({ secret: process.env.JWT_SECRET })) .hook('beforeHandle', async (ctx) => { const authHeader = ctx.headers.authorization; if (!authHeader) { ctx.set.status = 401; return 'Unauthorized'; } const token = authHeader.split(' ')[1]; try { ctx.store.user = await ctx.jwt.verify(token); } catch (err) { ctx.set.status = 403; return 'Forbidden: Invalid token'; } }); // Admin权限守卫插件 export const adminGuard = new Elysia() .hook('beforeHandle', (ctx) => { const user = ctx.store.user; if (!user || user.role !== 'admin') { ctx.set.status = 403; return 'Forbidden: Admin required'; } }); // 主应用使用 import { Elysia } from 'elysia'; import { authPlugin, adminGuard } from './auth.plugin'; const app = new Elysia() .use(authPlugin) // 全局启用认证 .get('/protected', (ctx) => ({ user: ctx.store.user })) .use(adminGuard) // 对后续路由启用admin权限校验 .get('/admin/dashboard', (ctx) => ({ message: 'Admin dashboard' })); app.listen(3000);
关键说明
ctx.store是请求隔离的,每个请求的store都是独立对象,不会出现跨请求数据污染。- 官方
@elysiajs/jwt插件比手动使用jsonwebtoken更贴合Elysia生态,支持异步验证和自动集成上下文。 - 钩子的执行顺序是按照注册顺序来的,确保认证钩子在权限校验钩子之前执行。
内容的提问来源于stack exchange,提问作者Shiraku Yanai
相关产品推荐
相关产品推荐

