文档目录
glen
· 预计阅读 3 分钟

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

本文导航
第 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
	}
}

几个容易忽略的细节:

  1. AccessPublic 不设置用户上下文。如果 Handler 里要用 ctx.Get("user"),不能用公开级别。
  2. 超管在 AccessSuperAdmin 之前就放行,所以超管能访问所有级别。
  3. AccessAuthenticated 不查 Casbin,只要令牌有效就放行。它适合「登录了就能看」的公共数据。
  4. AccessAuthorized 一定查 Casbin,权限标识来自运行时路由。
  5. 路由表里找不到路径时返回 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 或独立权限标识。

评论

加载评论中…

上一篇 第 2 章 登录与 JWT:令牌怎么来、怎么用 下一篇 第 4 章 权限标识与路由:app:module:方法名

BitLabs © 2026 · 论坛讨论 · 博客长文 · 文档手册