跳至正文
来两杯美式
返回

Auth Guard 与 tRPC 中间件

By 来两杯美式
发布于更新于

核心概念

概念层级作用
Auth Guard前端路由保护,未登录重定向
tRPC Middleware后端API保护,校验用户身份
Context共享请求级共享数据容器
请求 → Auth Guard(前端) → tRPC Middleware(后端) → Procedure
         ↓                        ↓
      页面重定向              返回 401 错误

Auth Guard(前端路由守卫)

作用

保护 Next.js 受保护路由(如 /dashboard),未登录用户重定向到登录页。

实现方式

Server Component 方式(推荐):

// app/dashboard/page.tsx
import { auth } from '~/server/auth'
import { redirect } from 'next/navigation'

export default async function DashboardPage() {
  const session = await auth()

  if (!session) {
    redirect('/auth/login')
  }

  return <div>Protected content</div>
}

Middleware 方式(全局路由保护):

// middleware.ts
import { type NextRequest, NextResponse } from "next/server";
import { auth } from "~/server/auth";

export async function middleware(request: NextRequest) {
  const session = await auth();

  if (!session && request.nextUrl.pathname.startsWith("/dashboard")) {
    return NextResponse.redirect(new URL("/auth/login", request.url));
  }

  return NextResponse.next();
}

特点


tRPC Middleware(后端 API 保护)

作用

保护 tRPC API 端点,验证用户 session 有效性,未登录返回 UNAUTHORIZED 错误。

实现机制

Context(上下文)

// src/server/api/trpc.ts
export const createTRPCContext = async (opts: { headers: Headers }) => {
  const session = await auth();
  return { db, session };
};

Middleware(中间件)

const timingMiddleware = t.middleware(async ({ next, path }) => {
  const start = Date.now();
  const result = await next();
  console.log(`[TRPC] ${path} took ${Date.now() - start}ms`);
  return result;
});

const isAuthed = t.middleware(({ ctx, next }) => {
  if (!ctx.session?.user) {
    throw new TRPCError({ code: "UNAUTHORIZED" });
  }
  return next({ ctx: { session: { ...ctx.session, user: ctx.session.user } } });
});

Procedure 定义

export const publicProcedure = t.procedure.use(timingMiddleware);
export const protectedProcedure = t.procedure
  .use(timingMiddleware)
  .use(isAuthed);

使用示例

// src/server/api/routers/resume.ts
export const resumeRouter = createTRPCRouter({
  // 公开接口(可访问但无用户信息)
  getBySubdomain: publicProcedure.query(...),

  // 受保护接口(必须登录)
  createResume: protectedProcedure.mutation(...),
  getMyResumes: protectedProcedure.query(...),
})

特点


哲学对比

设计模式相同

两者都基于责任链模式(Chain of Responsibility)

Java Filter vs tRPC Middleware

JavatRPC
FilterMiddleware
FilterChainProcedure chain
doFilter()next()
@WebFiltertRPC middleware

前端 Guard vs 后端 Middleware

维度Auth GuardtRPC Middleware
位置前端/路由层后端 API 层
触发页面导航API 请求
绕过难度易(DevTools)难(需伪造 token)
响应页面重定向401 错误
作用体验优化安全防护

最佳实践

双重保护

// 前端 Guard(体验)
if (!session) redirect("/auth/login");

// 后端 Middleware(安全)
export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
  if (!ctx.session?.user) throw new TRPCError({ code: "UNAUTHORIZED" });
  return next();
});

Context 缓存

// RSC 中使用 cache 避免重复创建
const createContext = cache(async () => {
  return createTRPCContext({ headers });
});

错误处理

// 统一错误格式化
errorFormatter({ shape, error }) {
  return {
    ...shape,
    data: { ...shape.data, zodError: error.cause instanceof ZodError ? ... : null }
  }
}

常见问题

Q: 为什么需要双重保护?

前端 Guard 改善用户体验,后端 Middleware 保证安全。两者各司其职,缺一不可。

Q: session 每次请求都查库?

auth() 实现决定。如果有缓存则无性能问题。

Q: middleware 中 throw 错误和 redirect 如何选择?

API 层 throw 401,页面层 redirect。职责分明。


分享这篇文章:
通过邮件分享这篇文章✓ 链接已复制
查看系列全部文章
  1. 01.T3 Stack 开发规范
  2. 02.Next.js 并行路由完全指南
  3. 03.Web 会话与身份验证完整指南
  4. 04.Auth Guard 与 tRPC 中间件
  5. 05.T3 Stack 会话认证指南
  6. 06.Next.js 子域名、反向代理与分享链路最佳实践
  7. 07.CORS 与 Web 安全实战白皮书
  8. 08.Web 多主题架构最佳实践
  9. 09.Vercel AI SDK 自定义 OpenAI 模型接入
  10. 10.AI 驱动动态看板架构指南
  11. 11.VitePress 动态渲染全栈落地方案

上一篇
T3 Stack 会话认证指南
下一篇
Web 会话与身份验证完整指南