第 2 章 登录与 JWT:令牌怎么来、怎么用
本文导航

第 2 章 登录与 JWT:令牌怎么来、怎么用
权限校验的第一步是识别「你是谁」。owl-admin 用用户名密码登录,签发 JWT,后续请求在 Authorization 头里带上令牌。
登录接口
POST /api/v1/users/login
Content-Type: application/json
{
"username": "admin",
"password": "123qwe"
}
成功响应:
{
"success": true,
"msg": "操作成功",
"data": {
"id": "19941996",
"username": "glen",
"nickname": "超级管理员",
"roles": [{ "name": "superAdmin" }],
"isSuperAdmin": true,
"accessToken": "eyJhbGciOiJIUzI1NiIs..."
}
}
data 里既有用户资料,也有 accessToken。
两种登录主体
1. 配置超管
用户名等于 conf/app.yaml 的 admin.username 时,不查数据库,直接构造内置超管:
admin:
username: admin
password: "$2a$10$..." # bcrypt hash
- 超管 ID 固定为
19941996; - 拥有
*:*:*; IsSuperAdmin: true;- 不受 Casbin 限制。
密码必须是 bcrypt hash。可以在「系统工具 / 密码工具」里计算密文;如果把入口切换为 ConsoleShell,也可以执行:
go run . admin:gen-pwd 你的明文密码
2. 数据库用户
普通用户从 admin_user 表读取,按用户名查询,并 Preload("Roles")。密码用 bcrypt 校验,失败返回登录错误。
令牌里有什么
type UserClaims struct {
model.User
jwt.RegisteredClaims
}
也就是说,JWT 的 payload 里包含:
- 用户基础字段(ID、用户名、昵称、头像、状态等);
- 角色列表;
isSuperAdmin;- 标准声明:
exp、iss。
密码字段的 JSON 标签是 json:"-",不会进入令牌。
令牌由 conf/jwt.yaml 配置:
signing-key: "your-secret-key"
issuer: "owl-admin"
expire: 3600
expire 单位是秒。
携带令牌
Authorization: Bearer <accessToken>
中间件会去掉 Bearer 前缀再解析:
token := ctx.Request.Header.Get("Authorization")
user, err := jwtService.ParseToken(strings.Replace(token, "Bearer ", "", -1))
解析成功后,用户信息会被放进:
ctx.Get("user"):完整的*model.User;- 请求上下文:
user_id、username、nickname、roles。
db.BaseModel 的 BeforeCreate / BeforeUpdate 会从上下文读 user_id、username,自动填充创建人、修改人。
登录后的三个接口
GET /api/v1/users/me 当前用户资料
GET /api/v1/users/me/menus 当前用户菜单
GET /api/v1/users/me/permissions 当前用户权限标识
curl "http://127.0.0.1:8080/api/v1/users/me" \
-H "Authorization: Bearer <token>"
curl "http://127.0.0.1:8080/api/v1/users/me/menus?app=admin" \
-H "Authorization: Bearer <token>"
curl "http://127.0.0.1:8080/api/v1/users/me/permissions" \
-H "Authorization: Bearer <token>"
- 超管:菜单返回全部(不含按钮),权限返回
["*:*:*"]; - 普通用户:菜单来自角色已分配的菜单,权限来自这些菜单上的
DependentsPermission。
/users/me/menus 支持 app 参数,多个子系统用逗号分隔:
GET /api/v1/users/me/menus?app=admin,cms
令牌过期与角色变更
- 令牌过期后,中间件解析失败,返回 401,需要重新登录;
- 修改用户角色后,旧令牌里的
roles不会立刻变化,但接口鉴权用的是数据库里的 Casbin 策略,所以权限会按新角色生效; - 前端缓存的权限列表可能仍旧,见第 6 章。
登录接口的保护
登录路由挂了两层保护:
r.Use(middleware.RateLimiter(time.Second*1, 2)).
Use(middleware2.LoginCaptchaGuard(userSvc, trustedDeviceRepo, captchaSvc)).
Post("/users/login", router.AccessPublic, userHandle.Login)
- 限流:每秒 2 次;
- 验证码:按配置和可信设备判断是否需要图形验证码;
- 登录成功会发布
UserLoginSuccess事件,写入登录日志。
请求体里的 deviceId 用于记录可信设备,captcha 用于提交验证码答案。
常见问题
登录返回「用户名或密码错误」
- 配置超管:检查
conf/app.yaml里的 bcrypt hash 是否对应明文密码; - 数据库用户:检查用户是否被禁用、密码是否被重置。
带令牌仍返回 401
- 请求头是否写成
Authorization: Bearer xxx; - 令牌是否过期;
signing-key是否在重启后变了(多实例部署必须一致)。
超管能访问,普通用户 403
这是正常现象,说明鉴权在工作。去检查普通用户的角色、角色菜单和 Casbin 策略,见第 5、6 章。
自检
- [ ] 能说清配置超管和数据库用户的区别;
- [ ] 知道 JWT 里包含哪些字段;
- [ ] 会用三个
me接口检查当前登录态; - [ ] 能定位 401 与 403 的不同原因。
评论
加载评论中…