Files
xuanzhi-service/server/.ai-specs/coding-specs/api-auth-control.md
wdh-home 8164eec650
Some checks failed
CI / init (push) Has been cancelled
CI / Frontend node 18.16.0 (push) Has been cancelled
CI / Backend go (1.22) (push) Has been cancelled
CI / devops-test (1.22, 18.16.0) (push) Has been cancelled
CI / release-pr (push) Has been cancelled
CI / release-please (push) Has been cancelled
CI / devops-prod (1.22, 18.x) (push) Has been cancelled
CI / docker (push) Has been cancelled
服务端
2026-04-22 15:49:50 +08:00

97 lines
5.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# API 鉴权与权限修改规范
## 适用范围
- 新增接口时,需要判定它是“公开访问”“仅登录可访问”还是“登录且有权限才可访问”时,使用本文。
- 已有接口在“不需要登录 / 需要登录 / 需要权限”之间切换时,使用本文。
## 现有鉴权基线
- `initialize/router.go` 当前只创建两类顶层路由组:
- `PublicGroup`:不挂鉴权中间件。
- `PrivateGroup`:统一挂 `middleware.JWTAuth()``middleware.CasbinHandler()`
- `JWTAuth` 定义在 `middleware/jwt.go`
- `CasbinHandler` 定义在 `middleware/casbin_rbac.go`
- 结论:
- 挂到 `PublicGroup` = 不需要登录。
- 挂到 `PrivateGroup` = 需要登录,且需要角色权限。
- 当前仓库没有默认内置的“只登录、不校验权限”公共组。
- 不存在“只校验权限、不登录”的合法模式;`CasbinHandler` 依赖 `JWTAuth` 写入的用户 claims。
## 权限模式判定
| 模式 | 路由挂载方式 | JWT | Casbin | 后续要求 |
|:---|:---|:---|:---|:---|
| 不需要登录 | `PublicGroup` / `PublicRouter` | 否 | 否 | 默认不作为角色权限点长期维护 |
| 需要登录,不需要权限 | 基于 `PublicRouter` 单独加 `middleware.JWTAuth()` | 是 | 否 | 默认不进入角色权限维护 |
| 需要登录且需要权限 | `PrivateGroup` / `Router` | 是 | 是 | 必须进入 `sys_apis` 并分配角色 |
## 强制规则
- 同一个 `path + method` 只能落一种鉴权模式;禁止同时注册到公开组和鉴权组。
- `middleware.OperationRecord()` 只负责操作审计,不改变登录与权限判定。
- 鉴权逻辑统一落在 `router` 层;禁止在 `API``Service` 中靠 `if token != ""``if authorityId == ...` 兜底模拟权限。
- 修改接口鉴权时Swagger 注解必须同步:
- 不需要登录:移除 `@Security ApiKeyAuth`
- 需要登录:保留 `@Security ApiKeyAuth`
- 业务路由统一在 `router/<module>` 调整;业务模块接入点统一在 `initialize/router_biz.go` 注册。
## 改成不需要登录
- 把路由挂载从 `PrivateGroup` 或“仅登录组”移到 `PublicRouter`
- 如果只是业务模块内部路由调整,不要改 `API``Service` 方法签名。
- 该接口如果不再作为后台角色权限点维护,必须把对应 `path + method` 从权限维护中移走,二选一即可:
- 删除对应 `sys_apis` 记录。
- 加入 `sys_ignore_apis`,避免后续 `SyncApi` 再次纳入权限页。
- 若历史上已经给该接口分配过角色,必须同步清理对应 Casbin 规则,避免后台权限配置出现“看起来要授权、实际上公开可调”的假象。
## 改成需要登录,不需要权限
- 当前项目没有全局现成的 `LoginGroup`;少量接口优先在对应 router 中基于 `PublicRouter` 单独挂 `JWTAuth`,不要为了一两个接口改全局入口。
- 推荐写法:
```go
func (r *BookRouter) InitBookRouter(Router *gin.RouterGroup, PublicRouter *gin.RouterGroup) {
bookLoginRouter := PublicRouter.Group("book").Use(middleware.JWTAuth())
{
bookLoginRouter.GET("profile", bookApi.GetProfile)
}
}
```
- 同一模块里如果“仅登录接口”很多,再考虑在 `initialize/router.go` 新增单独的 `LoginGroup`,并把它显式传入对应 router 初始化函数。
- 这类接口禁止挂 `middleware.CasbinHandler()`
- 这类接口默认不进入 `sys_apis` 的角色权限维护;如果历史上已经进入,必须删除或加入 `sys_ignore_apis`,不要继续让后台把它当作“需要分配角色”的接口。
## 改成需要登录且需要权限
- 把路由挂到 `PrivateGroup` 或业务 router 的 `Router` 参数上。
- 只把路由挪进 `PrivateGroup` 还不够;`CasbinHandler``path + method` 校验权限,没有角色策略就会直接返回“权限不足”。
- 因此必须同时完成以下动作:
- 确保该 `path + method` 存在于 `sys_apis`
- 确保该接口已分配允许访问的角色。
- 刷新 Casbin 策略缓存。
- 系统现成接口的完整路径 = `<router-prefix>/api/...`;如果 `router-prefix` 为空,则完整路径就是 `/api/...`。不要把是否存在额外前缀写死成项目事实。
- 常用管理接口:
- `GET <router-prefix>/api/syncApi`:对比内存路由和 `sys_apis`
- `POST <router-prefix>/api/enterSyncApi`:确认把新增路由写入 `sys_apis`
- `POST <router-prefix>/api/setApiRoles`:全量覆盖某个接口的角色列表
- `GET <router-prefix>/api/freshCasbin`:刷新 Casbin 策略缓存
- 若接口此前在 `sys_ignore_apis` 中,改成“需要权限”前必须先取消 ignore再同步到 `sys_apis`
- 若不给任何角色分配该接口,则结果不是“仅登录可访问”,而是“所有已登录用户都权限不足”。
## 验证要求
- 修改完成后,至少验证下面三类请求结果:
- 公开接口:不带 token 可访问成功。
- 仅登录接口:不带 token 返回未登录;带有效 token 可访问成功。
- 权限接口:不带 token 返回未登录;带无权角色 token 返回“权限不足”;带有权角色 token 可访问成功。
- 如果改了接口鉴权方式,同时该接口出现在 Swagger、前端权限配置页、角色配置页也必须同步核对展示结果是否一致。
## 禁止事项
- 禁止只改 `sys_apis``casbin_rule`,却不改真实 router 挂载。
- 禁止只把路由从 `PublicGroup` 挪到 `PrivateGroup`,却不补 `sys_apis` 和角色策略。
- 禁止把“仅登录接口”偷懒挂到 `PrivateGroup`,再靠给所有角色放权来模拟“无权限限制”。
- 禁止让公开接口长期保留在角色权限维护页里,造成权限含义失真。