第 3 章 访问级别:四种开关决定接口怎么校验
本文导航

第 3 章 访问级别:四种开关决定接口怎么校验
owl-admin 的每条路由都必须声明一个访问级别。它不是装饰,而是中间件的执行分支。选错级别,轻则功能不可用,重则接口裸奔。
四种级别
const (
AccessPublic AccessLevel = "开放接口"
AccessAuthenticated AccessLevel = "需要登录"
AccessAuthorized AccessLevel = "需要授权"
AccessSuperAdmin AccessLevel = "仅超管"
)
| 级别 | 需要令牌 | 校验 Casbin | 典型用途 |
|---|---|---|---|
AccessPublic |
否 | 否 | 登录、注册、公开配置、健康检查 |
AccessAuthenticated |
是 | 否 | 我的信息、我的菜单、下拉选项 |
AccessAuthorized |
是 | 是 | 绝大多数业务增删改查 |
AccessSuperAdmin |
是 | 否(直接判超管) | 系统配置、字典维护等敏感操作 |
中间件怎么走
permission_check.go 的核心逻辑:
if accessLevel == router.AccessPublic {
ctx.Next()
return
}
token := ctx.Request.Header.Get("Authorization")
user, err := jwtService.ParseToken(...)
if err != nil {
ctx.AbortWithError(http.StatusUnauthorized, ...)
return
}
ctx.Set("user", user)
// 把 user_id / username / roles 写进请求上下文
if user.IsSuperAdmin {
ctx.Next()
return
}
if accessLevel == router.AccessSuperAdmin {
ctx.AbortWithError(http.StatusForbidden, ...)
return
}
if accessLevel == router.AccessAuthenticated {
ctx.Next()
return
}
if accessLevel == router.AccessAuthorized {
can, err := enforcer.Enforce(cast.ToString(user.ID), api.Permission)
if !can {
ctx.AbortWithError(http.StatusForbidden, ...)
return
}
}
几个容易忽略的细节:
AccessPublic不设置用户上下文。如果 Handler 里要用ctx.Get("user"),不能用公开级别。- 超管在
AccessSuperAdmin之前就放行,所以超管能访问所有级别。 AccessAuthenticated不查 Casbin,只要令牌有效就放行。它适合「登录了就能看」的公共数据。AccessAuthorized一定查 Casbin,权限标识来自运行时路由。- 路由表里找不到路径时返回 404,即使 Gin 里注册了该路径,只要不是通过 Builder 注册,中间件也可能不认。
怎么选级别
用 Public
- 登录、注册、发送验证码;
- 公开文章列表、公开配置;
- 第三方回调;
- 健康检查。
原则:不依赖登录态,且泄露后无害。
用 Authenticated
/users/me、/users/me/menus、/users/me/permissions;- 角色选项、岗位选项、字典项;
- 任何登录后都要用、但不涉及敏感操作的下拉数据。
原则:需要知道是谁,但不需要细分谁能看。
用 Authorized
- 业务数据的增删改查;
- 分配角色、分配菜单;
- 导入导出。
原则:需要按角色控制。这是默认选择,也是唯一会自动生成按钮菜单的级别。
用 SuperAdmin
- 系统配置读写;
- 字典维护;
- 可能影响全局的开关。
原则:只允许内置超管或明确标记为超管的账号。注意:这个级别不查 Casbin,普通角色即使被分配了对应按钮也无法访问。
实际路由对照
来自 owl-admin/app/route/api.go:
r.Post("/users/login", router.AccessPublic, userHandle.Login)
r.Get("/users/me", router.AccessAuthenticated, userHandle.Me)
r.Get("/users/me/menus", router.AccessAuthenticated, userHandle.GetMyMenus)
r.Get("/users/me/permissions", router.AccessAuthenticated, userHandle.GetMyPermissions)
r.Post("/users", router.AccessAuthorized, userHandle.Create)
r.Get("/users", router.AccessAuthorized, userHandle.Retrieve)
r.Get("/setting", router.AccessSuperAdmin, settingHandle.Retrieve)
r.Put("/setting", router.AccessSuperAdmin, settingHandle.Update)
r.Get("/setting/public", router.AccessPublic, settingHandle.Public)
一个常见误区
「我把接口改成 AccessPublic,为什么前端不带令牌也能访问,但拿不到用户信息?」
因为公开级别不解析令牌,也不注入用户上下文。如果 Handler 里需要当前用户,应该用 AccessAuthenticated,而不是在 Handler 里自己解析令牌。
自检
- [ ] 能背出四种级别各自校验什么;
- [ ] 知道只有
AccessAuthorized会生成按钮菜单; - [ ] 知道公开接口不会注入用户上下文;
- [ ] 新增敏感接口时优先考虑
AccessSuperAdmin或独立权限标识。
评论
加载评论中…