Django Allauth / Ninja 后端 搭配 Tanstack 前端的避坑 Tips
在这篇文章中,我想与大家分享我使用 Django Allauth 无头(Headless)后端认证,与 TanStack Start前端结合使用的经验和心得。我今年才刚刚学习 Web 编程,定有不少错漏,还望读者指正。
演示项目代码库
https://github.com/sd44/django-allauth
项目背景
Life is short, you need Python
虽然目前前后端分离架构正被 TypeScript 一体化框架(如Next.js/Tanstack Start等)冲击,特别是在要求全栈协作、类型一致性、小团队敏捷项目上,但我仍爱 Python Django 的简洁清晰、快速开发和易于维护。本文无意也无力讨论架构的优劣,就此打住吧。
Django Allauth 与 NextAuth.js, Better Auth都提供多种认证方式(如手机号码、邮箱、通行密钥、数十种社交账户认证等)。但官方 Allauth Headless + React SPA示例仍然是 JS,而非 TS 代码;网络上也缺乏Allauth 对接 Tanstack/Next SSR前端的教程。本文便由此而生,但限于篇幅,只提出个别避坑指南,并不完整。
技术栈
- 后端:
Django,Django Allauth,Django Ninja - 前端:
React,TanStack Start/Query,Orval - 数据库:
Django支持的多种数据库均可,它也提供了近乎完美的数据库迁移指令
步骤概述
1. 设置 Django 后端
安装后端
Django 和 Django Allauth, Django Ninja 等的安装,参见官方文档和代码库 backend/pyproject.toml, backend/mysite/settings.py。
Django 提供总体管理,Ninja 提供其他自定义 API。其中 settings.py 中需要注意如下几点。
前后端、社交账户如 GitHub Google 等相关 URL 配置必须完全一致:
- 协议(
http与https不同) - 域名(
localhost与127.0.0.1不同) - 端口(
3000、8000不同) - 路径,包含或不包含最后的
/不同
CORS 和 CSRF 安全设置
我前端服务器端口为 3000
1 | # DANGER: allow all origins |
配置 AllAuth 和 Ninja 提供 openapi 文件
安装 PyYAML 依赖,并配置如下,以便提供/_allauth/openapi.yaml, /_allauth/openapi.json and /_allauth/openapi.html
1 | HEADLESS_SERVE_SPECIFICATION = True # |
Ninja OpenApi 文件则是 /api/openapi.json
2. 创建前端项目
安装
前端安装见相关文档和我的代码库 frontend/package.json,略。虽然我使用的是Tanstack Start,但想必 Next.js 中也有类似功能实现。
通过 OpenApi 文件自动生成接口和类型
有很多软件包可以实现根据 OpenApi 文件生成相应类型和接口,我选用了功能强大的Orval,可方便结合Axios与Tanstack Query,参考如下配置:
1 | const BASE_URL = 'http://localhost:8000'; |
运行orval命令就会自动生成了
1 | bunx orval |
获取 CSRFToken
OAuth 除个别 GET 操作外,基本都需要同步传递 CSRFToken,默认存储在 cookie 中。因此我们需要获取它,并将其放入默认的 Http 客户端实例中。
Tanstack start 提供有 getCookie函数,我们直接使用。
1 | import { createServerFn } from '@tanstack/react-start'; |
Http Verb 携带 CSRFToken
1 | const csrftoken = await getCSRFTokenByCookie(); |
社交账户 Auth Redirect
由于社交账户认证重定向会导致用户面临重定向(302),因此此调用仅在浏览器中可用,并且必须以同步(非 XHR)方式调用, enctype 为 application/x-www-form-urlencoded.这也就意味着Orval生成的axios/fetch 等异步 JS 调用方式无法使用,需要我们自己实现,如下代码源于 AllAuth React-SPA 官方示例:
1 | export function postForm(action: string, data: Record<string, string>) { |
路由守卫
传统的路由守卫过于笨重,注册、登录、注销、用户认证状态等集于一身,现在已经是 5202 年了,我们使用更现代更低耦合的方法吧。幸好 Tanstack Start 提供了强大的路由功能可以方便完成以上功能。。
在根路由上下文中存储当前 session ,以获取用户状态:
1 | createTanStackRouter({ |
1 | export const Route = createRootRouteWithContext<{ |
如此,我们就可以在每个路由获取上下文中的当前 session 数据,已确定用户是否登陆。如
1 | export const Route = createFileRoute('/')({ |
除获取用户信息外 index.tsx 文件中,还有其他相关内容,这里尤其注意 登录、注销、注册等操作完成后使 queryClient 之前获取的相应数据失效,必要时也要使路由失效(例如注销操作)。可详细看下 index.tsx, 在此不表。
总结
祝您开发过程更加顺畅。