ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

开源项目 Spring Boot + React (Ant Design Pro) 配 TaoToken:快速构建后台管理系统

开源项目 Spring Boot + React (Ant Design Pro) 配 TaoToken:快速构建后台管理系统 1. 从零搭后台管理系统真正卡住人的不是代码Spring Boot React (Ant Design Pro) 这套组合几乎是国内后台管理系统的默认答案。后端 Spring Boot 提供 REST 接口、Spring Security 做鉴权前端 Ant Design Pro 提供开箱即用的中后台布局、权限菜单和请求层。听起来很顺但真从零跑一遍你会发现卡点根本不在写业务代码而在这些地方JWT 的 access token 和 refresh token 怎么配合、前端请求层怎么统一注入 token、401 之后怎么自动刷新而不是把用户踢回登录页、前后端分离时跨域和代理怎么配、以及最要命的——大模型接口的 Key 散落在各个配置文件里换一个模型就要改一遍代码。这篇就按「Spring Boot React (Ant Design Pro) 开源脚手架」这条链路把后台管理系统从零搭起来重点交付三样能直接复制的东西application.yml配置骨架、Ant Design Pro 的settings.json与请求层封装片段、以及登录鉴权和接口联调的验证动作。同时把统一 Key/API 通道接进来让后端调用大模型时不用在每个业务里硬编码密钥。适合谁看已经会一点 Java 和 React、想快速起一个带 RBAC 权限的后台管理系统的人或者手上有个开源脚手架但前后端联调总是卡在鉴权和请求封装上的开发者。下面所有配置都以「能跑起来、能验证」为标准不堆概念。2. 前置准备脚手架、环境与统一 Key 通道2.1 脚手架选型与目录结构开源项目 Spring Ant 这类脚手架的价值在于它已经把后台管理系统的核心架子搭好了——目录结构、基于角色的访问控制、OpenAPI、统一日志和 Error 处理。前后端分别 Run 起来一个后台管理系统的骨架就成型了。它的代码集中在 core 文件夹下很轻量这点对后续让 AI 在稳定底盘上写业务很关键。后端核心目录大致是这样src/main/java/com/example/ ├── core/ # 框架核心安全、异常、日志、分页 ├── modules/ # 业务模块 └── Application.java前端基于 Ant Design Pro请求层、权限、布局都在src/下。你要做的是在这个底盘上接业务而不是从零造框架——从零搭的框架很难保证质量和有没有安全问题。2.2 环境版本对齐版本不一致是联调翻车的高频原因先把这张表对一遍组件建议版本说明JDK17Spring Boot 3.x 要求Spring Boot3.2.x与 Spring Security 6 配套Node.js18 LTSAnt Design Pro 要求Ant Design Pro6.x基于 Umi 4MySQL8.0或按脚手架默认2.3 统一 Key/API 通道的接入位置后台管理系统里大模型能力通常出现在两个地方一是运营侧的智能助手、内容生成二是系统内的数据摘要、工单分类。如果每个业务模块都自己读 Key、自己拼请求地址后面换模型、加限流、做审计就会非常痛苦。正确做法是在后端 core 层做一个统一的模型调用客户端所有业务只依赖这个客户端。TaoToken 提供的就是这样一个统一入口官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 。你只需要在配置里维护一个 base URL 和一个 Key业务代码不感知具体模型供应商。先去控制台创建 Keyhttps://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 拿到 Key 之后不要写进代码走环境变量或配置中心。Key 的管理页面在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 接入细节看文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。3. 可复制配置application.yml 与请求层封装3.1 后端 application.yml 骨架这份配置覆盖数据源、JWT、以及统一模型通道。JWT 部分把 access token 和 refresh token 的有效期分开这是脚手架里基于 JWT 认证的标准做法。server: port: 8080 servlet: context-path: /api spring: datasource: url: jdbc:mysql://localhost:3306/spring_ant?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: ${DB_PASSWORD} driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update show-sql: false # JWT 配置access token 短、refresh token 长 security: jwt: secret: ${JWT_SECRET} access-token-expire: 1800 # 30 分钟 refresh-token-expire: 604800 # 7 天 header: Authorization prefix: Bearer # 统一模型通道业务代码只认这个 base-url taotoken: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} default-model: claude-sonnet-4-5 timeout: 60000注意api-key用的是${TAOTOKEN_API_KEY}占位符实际值通过环境变量注入。这样即使配置文件进了 Git密钥也不会泄露。default-model只是默认值业务侧可以覆盖。3.2 后端统一调用客户端在 core 层写一个客户端把 base URL、鉴权头、超时、错误处理都收口。业务模块注入它即可不直接碰 HTTP。Component public class ModelClient { private final RestClient restClient; private final String defaultModel; public ModelClient(Value(${taotoken.base-url}) String baseUrl, Value(${taotoken.api-key}) String apiKey, Value(${taotoken.default-model}) String defaultModel) { this.defaultModel defaultModel; this.restClient RestClient.builder() .baseUrl(baseUrl) .defaultHeader(Authorization, Bearer apiKey) .defaultHeader(Content-Type, application/json) .build(); } public String chat(String prompt) { MapString, Object body Map.of( model, defaultModel, messages, List.of(Map.of(role, user, content, prompt)) ); return restClient.post() .uri(/v1/chat/completions) .body(body) .retrieve() .body(String.class); } }这段代码的关键点是baseUrl和apiKey都来自配置业务侧调用modelClient.chat(...)时完全不知道背后是哪家模型。以后要换模型改default-model就行。3.3 前端 settings.json 与请求层封装Ant Design Pro 的config/settings.json控制布局和主题先给一份能用的{ navTheme: light, layout: mix, contentWidth: Fluid, fixedHeader: true, fixSiderbar: true, pwa: false, title: 后台管理系统, token: { header: { colorBgHeader: #001529 } } }请求层是前后端分离联调的核心。Ant Design Pro 默认用 umi-request我们要做三件事请求自动带 token、响应统一处理 errorCode、401 时用 refresh token 换新 token 并重放请求。// src/utils/request.js import { extend } from umi-request; import { getToken, setToken, clearToken } from ./auth; const request extend({ prefix: /api, timeout: 15000, errorHandler: (error) { const { response } error; if (response response.status 401) { return refreshAndRetry(error); } throw error; }, }); request.interceptors.request.use((url, options) { const token getToken(); return { url, options: { ...options, headers: { ...options.headers, Authorization: token ? Bearer ${token} : , }, }, }; }); let refreshing null; async function refreshAndRetry(error) { if (!refreshing) { refreshing fetch(/api/auth/refresh, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ refreshToken: getToken(refresh) }), }) .then((res) res.json()) .then((data) { setToken(data.accessToken); return data.accessToken; }) .finally(() { refreshing null; }); } const newToken await refreshing; const { options } error.request; return request(error.request.url, { ...options, headers: { ...options.headers, Authorization: Bearer ${newToken} }, }); } export default request;这里用了一个refreshing变量做并发控制多个请求同时 401 时只发一次刷新请求其余等结果。这是踩过的坑——不做并发控制刷新接口会被打爆甚至因为 refresh token 被重复使用而失效。4. 验证请求登录鉴权与接口联调4.1 启动与登录验证后端mvn spring-boot:run前端npm run dev。先验证登录接口curl -X POST http://localhost:8080/api/auth/login \ -H Content-Type: application/json \ -d {username:admin,password:admin123}成功返回里应该有accessToken和refreshToken两个字段。把 accessToken 拿去请求受保护接口curl http://localhost:8080/api/admin/page \ -H Authorization: Bearer accessToken用 admin 账号能拿到数据换成普通 user 账号应该返回 403 或对应的 errorCode。这一步验证的就是基于角色的访问控制是否生效——左侧管理员能访问 admin page、admin sub-page、admin button普通用户则无权限。4.2 验证统一模型通道后端加一个测试接口调用ModelClientcurl -X POST http://localhost:8080/api/ai/ping \ -H Authorization: Bearer accessToken \ -H Content-Type: application/json \ -d {prompt:用一句话说明什么是后台管理系统}如果返回了模型输出说明taotoken.base-url和api-key配置正确统一通道打通。想单独验证模型对话可以直接用模型对话页面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchatutm_campaignrewrite 试一下同一个 Key确认 Key 本身可用。4.3 验证 token 刷新链路把 access token 有效期临时改成 60 秒登录后等过期再点一个需要鉴权的菜单。预期行为是请求先 401前端自动调 refresh 接口拿到新 token 后重放原请求用户无感知。如果被踢回登录页说明刷新逻辑没生效回去检查refreshAndRetry里的并发控制和 refresh token 是否正确传递。5. 本篇常见错排查跨域报错 CORS前后端分离时前端prefix: /api走的是 Umi 代理。检查config/proxy.ts是否把/api指向了http://localhost:8080。生产环境用 Nginx 反代不要在后端无脑开CrossOrigin(*)。401 循环刷新refresh 接口本身也返回 401 时refreshAndRetry会再次触发刷新形成死循环。要在刷新失败时直接clearToken()并跳登录页别让它继续重试。JWT 签名不匹配security.jwt.secret长度不够或前后端不一致都会导致签名校验失败。HS256 要求密钥至少 256 位用一段足够长的随机字符串别用123456。模型接口超时taotoken.timeout默认 60 秒长文本生成可能不够。但也不要无脑调大配合前端 loading 状态管理避免请求进行中用户继续操作。局部 loading 和全局遮罩要区分开。errorCode 和 HTTP 状态码混淆脚手架做了基于 errorCode 和 showType 的统一异常处理同时对网络错误和 HTTP 层级错误也做了统一处理。前端拦截器里判断 401 要看 HTTP 状态码业务错误看响应体里的 errorCode两者别混。分页参数对不上Ant Design Pro 的 ProTable 默认传current和pageSize后端如果用的是page和size要在请求层做一次映射否则永远返回第一页。6. 后续怎么接从脚手架到业务落地底盘跑通之后真正的工作是往modules/里加业务。这时候 AI Coding 的价值就体现出来了前后端都有 AGENTS.md它让 AI 知道框架核心在 core 文件夹下、以及前后端怎么对接。你可以直接和 AI 说「我有 xx 业务有 xx 和 xx 两个角色分别有什么权限和功能」AI 能理解已设计好的 RBAC 权限架构创建业务账号、业务表结构和代码。但前提是底盘可靠。让 AI 从零搭框架很难保证质量和有没有安全问题让 AI 在 Spring Ant 这种核心架构上开发底盘已经可靠可控AI 只专注业务实现。这也是为什么统一 Key 通道要放在 core 层——业务代码越干净AI 生成的东西越不容易跑偏。如果你要长期在这个项目上做编码和 Agent 集成可以看下 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 它更适合持续性的开发场景。Claude Code 相关的接入方式在 https://taotoken.net/claude-code?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecodeutm_campaignrewrite 有说明。接入文档统一在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 遇到鉴权或请求格式问题先翻这里。最后留一个实操建议把application.yml里的access-token-expire先设成 120 秒完整走一遍「登录 → 访问 → 过期 → 自动刷新 → 重放」的链路确认无误后再改回 30 分钟。这个动作能帮你提前发现 90% 的鉴权联调问题比事后在线上排查省事得多。
返回列表