- 运维观测
- 指标监控
- 告警
【免费下载链接】falcon-plus
An open-source and enterprise-level monitoring system.
导读
本文围绕 Open-Falcon(falcon-plus)监控系统中 api 模块提供的POST /api/v1/admin/login管理员 SSO(Single Sign-On)登录接口展开,讲解它的请求/响应格式、Session 鉴权前提、管理员权限校验逻辑,并结合源码剖析其在 modules/api/app/controller/uic/session_controller.go 中的真实实现。读完本文,你将掌握如何用已有的 Session 令牌换取任意用户(含普通用户)的登录态、理解role权限模型在接口中的作用,并能通过测试用例与配置项完整复现该接口的调用方式。
接口总览:文档中的核心定义
原文档(docs/_posts/Admin/2017-12-07-admin_login.md)将该接口定义为:
- 接口路径:
POST /api/v1/admin/login - 接口类型:
POST - 用途:SSO 登入(管理员代登录)
- 前置条件:
- 需要携带有效的 Session(认证会话);
- 属于
Admin管理能力的使用场景。
请求体(Request)
接口只接收一个 JSON 字段——目标用户名,例如为名为test2的用户发起登录:
{ "name": "test2" }响应体(Response)
成功时返回 HTTPStatus: 200,响应 JSON 中包含三个字段:
{ "sig": "9d791331c0ea11e690c5001500c6ca5a", "name": "test2", "admin": false }字段说明:
| 字段 | 类型 | 含义 |
|---|---|---|
sig | string | 该用户在当前系统中生成的 Session 签名(令牌),后续所有需鉴权接口通过Apitoken请求头携带 |
name | string | 被代登录的用户名,与请求体中的name一致 |
admin | bool | 该用户是否为管理员(由角色role决定,详见下文权限模型) |
错误响应遵循统一的 response status codes 文档 约定。
路由注册与中间件:为什么必须先有 Session
在 modules/api/app/controller/uic/user_routes.go 中可以看到该接口的路由注册方式:
adminapi := r.Group("/api/v1/admin") adminapi.Use(utils.AuthSessionMidd) adminapi.PUT("/change_user_role", ChangeRoleOfUser) adminapi.PUT("/change_user_passwd", AdminChangePassword) adminapi.PUT("/change_user_profile", AdminChangeUserProfile) adminapi.DELETE("/delete_user", AdminUserDelete) adminapi.POST("/login", AdminLogin)关键点在于adminapi.Use(utils.AuthSessionMidd):整个/api/v1/admin路由组都挂在Session 鉴权中间件之下。也就是说,调用/api/v1/admin/login之前,调用方必须以管理员身份完成一次常规登录,拿到自己的sig,然后以如下请求头发起本接口:
"Apitoken": "{\"name\":\"root\",\"sig\":\"427d6803b78311e68afd0242ac130006\"}"这与普通用户登录接口POST /api/v1/user/login(session_controller.go)形成对照:
| 对比项 | /api/v1/user/login | /api/v1/admin/login |
|---|---|---|
| 请求字段 | name+password | 仅name(由管理员代发起) |
| 是否需要 Session | 否,首次登录 | 是,需携带管理员Apitoken |
| 鉴权模型 | 校验密码哈希 | 校验管理员身份 + 角色等级 |
中间件如何校验 Session
中间件 modules/api/app/utils/auth_middle.go 调用h.SessionChecking:
- 从请求头解析
Apitoken,反序列化为{name, sig}; - 若配置了
default_token且sig与之相等,直接视为通过(用于服务端内部访问); - 否则在 uic 库的
user表与session表中核对name与sig的匹配关系,session表存在该记录才判定认证成功; - 若配置
skip_auth为true,则跳过校验(仅建议在开发/测试环境开启)。
核心实现解读:AdminLogin 的完整逻辑
处理函数位于 modules/api/app/controller/uic/session_controller.go,其执行流程如下:
- 绑定请求参数:
APIAdminLoginInput只声明了Name字段并标记binding:"required",即name为空时直接返回400("name is blank")。
type APIAdminLoginInput struct { Name string `json:"name" form:"name" binding:"required"` }解析当前调用者身份:通过
h.GetUser(c)从Apitoken中还原出当前登录用户(即发起代登录的管理员)。查询目标用户:按
name在 uic 库中查询被登录用户,若user.ID == 0表示用户不存在,返回"no such user"。权限等级校验(关键安全点):
case user.Role >= adminuser.Role: h.JSONR(c, badstatus, "API_USER not admin, no permissions can do this") return从代码可以推断:被登录用户的role必须小于当前调用者的role,否则拒绝操作并返回"API_USER not admin, no permissions can do this"。这意味着管理员不能通过本接口登录与自己同级或更高级别的账号,防止越权。
Session 查询与生成:在
session表中按uid查询目标用户的既有 Session;若不存在(session.ID == 0),则生成新的 UUID 作为sig,并将过期时间设为当前时间 +3600*24*30秒(30 天),写入数据库。若目标用户已有 Session,则直接复用,避免重复生成。返回结果:响应的
sig、name、admin三个字段与文档示例完全一致。
resp := struct { Sig string `json:"sig,omitempty"` Name string `json:"name,omitempty"` Admin bool `json:"admin"` }{session.Sig, user.Name, user.IsAdmin()} h.JSONR(c, resp)其中admin字段由 modules/api/app/model/uic/user.go 的IsAdmin()决定:
func (this User) IsAdmin() bool { if skipAccessControll() { return true } if this.Role == 2 || this.Role == 1 { return true } return false }即:关闭access_control配置时所有用户都算管理员;开启后role为1(管理员)或2(超级管理员)才返回true。这正是文档示例中test2用户返回"admin": false的原因——该用户role小于管理员等级。
Session 认证链路回顾
/api/v1/admin/login返回的sig就是目标用户的会话令牌,后续调用其他需要鉴权的接口时,将它放入Apitoken请求头即可,整个认证链路为:
- 管理员先调用
POST /api/v1/user/login(name+password)获取自己的{name, sig}; - 携带该
Apitoken调用POST /api/v1/admin/login,传入目标用户名; - 拿到目标用户的
{sig, name, admin},此后以目标用户身份访问受保护接口; - 受保护接口通过
AuthSessionMidd中间件在user/session表中校验令牌有效性。
这与 Auth Session 文档 描述的Apitoken验证方式一致:请求头中Apitoken的 JSON 结构为{"name":"root","sig":"..."},并建议携带X-Forwarded-For。若 Session 有效,GET /api/v1/user/auth_session会返回{"message":"session is vaild!"}。
测试用例:接口行为的自动化验证
仓库自带的 API 测试 modules/api/test/api_test.go 对该接口做了完整验证:
Convey("Admin login user: POST /admin/login", t, func() { *rr = map[string]interface{}{} resp, _ := resty.R(). SetHeader("Content-Type", "application/json"). SetHeader("Apitoken", api_token). SetBody(map[string]string{ "name": test_user_name, }). SetResult(rr). Post(fmt.Sprintf("%s/admin/login", api_v1)) So(resp.StatusCode(), ShouldEqual, 200) So(*rr, ShouldNotBeEmpty) So((*rr)["name"], ShouldEqual, test_user_name) })从测试可以看出:
- 请求必须携带
Apitoken请求头(测试中api_token由 root 用户登录后构造,格式为{"name":"root","sig":"..."}); - 请求体为
{"name": "<目标用户名>"}; - 断言响应
200、响应体非空且name与请求用户名一致。
同一测试文件中也通过POST /user/login验证了 root 用户登录返回sig且admin为true,可以作为调用本接口前的完整前置流程参考。
常见问题与注意事项
name字段缺失或为空:接口直接返回400,错误信息为"name is blank"。- 目标用户不存在:返回
"no such user"。 - 权限不足:若被登录用户
role >=当前调用者role,返回"API_USER not admin, no permissions can do this"。因此超级管理员(role=2)不能通过该接口登录,而管理员(role=1)可以代登录普通用户(role=0)。 - Session 复用:目标用户已有未过期 Session 时直接复用,不会重复生成
sig,因此多次调用同一接口可能返回相同的sig。 - 测试环境便利开关:
skip_auth为true时中间件不做 Session 校验;access_control为false时IsAdmin()恒为true,两者都只建议用于开发调试,生产环境请保持默认的安全配置。
小结
POST /api/v1/admin/login是 Open-Falcon api 模块中实现管理员 SSO 代登录的关键接口:它在 Session 中间件的保护下,仅凭目标用户名即可换取该用户的会话令牌,并通过role等级比较确保操作不越权。结合 路由注册、控制器实现、Session 校验逻辑 与 API 测试用例,你可以完整复现该接口的调用流程,并为基于 Open-Falcon 的二次开发(如统一登录门户、用户管理后台)提供可靠的接入参考。
- 运维观测
- 指标监控
- 告警
【免费下载链接】falcon-plus
An open-source and enterprise-level monitoring system.
相关推荐
Cua 项目 GitHub WIF 认证的 Fleets 冒烟测试池:Terraform 基础设施与每日 CI 验证实战
Cua 项目 GitHub WIF 认证的 Fleets 冒烟测试池:Terraform 基础设施与每日 CI 验证实战 导读 本文围绕仓库 infra/fle
运维观测指标监控告警WebGoat 发布流程实战指南:从版本号规范到 Maven 构建、Tag 推送与 GitHub Release 发布
WebGoat 发布流程实战指南:从版本号规范到 Maven 构建、Tag 推送与 GitHub Release 发布 导读 本文以 WebGoat 仓库根目录
运维观测指标监控告警Notepad--:跨平台轻量级文本编辑器的 6 个实战场景
Notepad :跨平台轻量级文本编辑器的 6 个实战场景 你大概也有过这种时刻:改一个 nginx.conf、翻一段服务器日志、对比两份配置,却不想为此打开一
运维观测指标监控告警
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考