适用于Azure Functions v4/v5的TypeScript JWT中间件方案咨询
在TypeScript Azure Functions中实现应用级JWT认证中间件模式
不用依赖久未更新的第三方包,你可以通过两种原生/轻量方式实现全局JWT认证,完美适配Azure Functions的特性:
方式一:通用Handler封装(兼容性强,支持所有版本)
这种方式把认证逻辑封装成高阶函数,给所有需要认证的函数统一套一层,也能轻松实现全局覆盖。
步骤1:安装依赖
npm install jsonwebtoken @types/jsonwebtoken
步骤2:编写认证中间件
创建src/middlewares/authMiddleware.ts文件:
import { Context, HttpRequest } from "@azure/functions"; import jwt from "jsonwebtoken"; // 从Azure Functions应用配置中读取密钥,禁止硬写在代码里 const JWT_SECRET = process.env.JWT_SECRET || "临时 fallback 密钥"; // 定义带用户信息的请求类型 type AuthenticatedHttpRequest = HttpRequest & { user: { userId: string; email: string } }; type AuthHandler = (context: Context, req: AuthenticatedHttpRequest) => Promise<void>; // 高阶函数:接收原始handler,返回带认证逻辑的新handler export const withAuth = (handler: AuthHandler) => { return async (context: Context, req: HttpRequest) => { try { // 提取Authorization头里的Bearer令牌 const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith("Bearer ")) { context.res = { status: 401, body: "请提供有效的Bearer认证令牌" }; return; } const token = authHeader.split(" ")[1]; // 验证JWT,可按需添加iss/aud等验证参数 const decodedUser = jwt.verify(token, JWT_SECRET) as { userId: string; email: string }; // 把用户信息挂载到req上,供后续业务逻辑使用 const authenticatedReq = req as AuthenticatedHttpRequest; authenticatedReq.user = decodedUser; // 执行原始业务handler await handler(context, authenticatedReq); } catch (err) { context.res = { status: 403, body: "令牌无效或已过期" }; } }; };
步骤3:在函数中使用(或全局统一配置)
单个函数使用:
// src/functions/protectedFunction.ts import { Context, HttpRequest } from "@azure/functions"; import { withAuth } from "../middlewares/authMiddleware"; async function protectedHandler(context: Context, req: HttpRequest & { user: any }) { context.res = { body: `欢迎回来,${req.user.email}!你的用户ID是${req.user.userId}`, }; } // 导出经过认证封装的函数 export default withAuth(protectedHandler);
如果要全局所有函数统一应用认证,可以通过入口文件批量封装:
// src/index.ts import { Context, HttpRequest, AzureFunction } from "@azure/functions"; import { withAuth } from "./middlewares/authMiddleware"; // 导入所有业务函数 import protectedFunc1 from "./functions/protectedFunc1"; import protectedFunc2 from "./functions/protectedFunc2"; import publicFunc from "./functions/publicFunc"; // 不需要认证的公开接口 // 映射函数名到处理逻辑,统一封装需要认证的函数 const functionHandlers: Record<string, AzureFunction> = { protectedFunc1: withAuth(protectedFunc1), protectedFunc2: withAuth(protectedFunc2), publicFunc: publicFunc, // 公开接口直接使用原始handler }; // 统一入口处理请求 export default async function (context: Context, req: HttpRequest) { const targetHandler = functionHandlers[context.executionContext.functionName]; if (!targetHandler) { context.res = { status: 404, body: "请求的函数不存在" }; return; } await targetHandler(context, req); }
方式二:官方Middleware机制(Azure Functions v4+)
如果用的是v4及以上版本的Azure Functions,可以用官方提供的Middleware特性,写法更贴近Express中间件,支持全局注册。
步骤1:确保依赖版本
npm install @azure/functions@latest
步骤2:编写认证中间件
// src/middlewares/authMiddleware.ts import { Middleware, Context, HttpRequest } from "@azure/functions"; import jwt from "jsonwebtoken"; const JWT_SECRET = process.env.JWT_SECRET || "临时 fallback 密钥"; export const authMiddleware: Middleware = async (context, req, next) => { try { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith("Bearer ")) { context.res = { status: 401, body: "请提供有效的Bearer认证令牌" }; return; } const token = authHeader.split(" ")[1]; const decodedUser = jwt.verify(token, JWT_SECRET) as { userId: string; email: string }; (req as any).user = decodedUser; // 执行下一个中间件或业务handler await next(); } catch (err) { context.res = { status: 403, body: "令牌无效或已过期" }; } };
步骤3:全局注册中间件并配置函数
// src/index.ts import { app } from "@azure/functions"; import { authMiddleware } from "./middlewares/authMiddleware"; // 注册全局中间件,所有HTTP函数都会先执行认证逻辑 app.use(authMiddleware); // 注册需要认证的函数 app.http("protectedFunc", { methods: ["GET"], authLevel: "anonymous", // 这里设为匿名,因为我们自己处理认证 handler: async (context, req) => { return { body: `欢迎回来,${(req as any).user.email}!你的用户ID是${(req as any).user.userId}`, }; }, }); // 注册不需要认证的公开函数 app.http("publicFunc", { methods: ["GET"], authLevel: "anonymous", handler: async () => { return { body: "这是公开访问的接口" }; }, });
一些最佳实践
- 密钥管理:JWT密钥一定要存在Azure Functions的应用配置(Configuration)里,绝对不能硬编码
- 细化错误处理:可以根据
jsonwebtoken抛出的错误类型,返回更具体的提示(比如令牌过期、签名无效等) - 权限扩展:如果需要细粒度权限控制,可以在JWT的payload里加入角色信息,在中间件中验证角色
- 排除特定函数:全局认证时,可以通过
context.executionContext.functionName判断,跳过不需要认证的函数
内容的提问来源于stack exchange,提问作者Aiden Dipple
相关产品推荐
相关产品推荐

