认证与权限
企智通的安全模型围绕**「你是谁」(认证)与「你能做什么」(授权)两层展开。认证基于 JWT 双令牌机制,授权基于 Casbin RBAC,并叠加数据权限范围**控制。本文详细介绍这套机制。
整体模型概览
┌─────────────────────────────────────────────────────────┐
│ 认证 (Authentication) │
│ "你是谁" —— 通过账号密码 / 扫码证明身份 │
│ │
│ 登录成功 → 签发 access_token + refresh_token (JWT) │
└────────────────────────────┬────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ 授权 (Authorization) │
│ "你能做什么" —— 三层校验 │
│ │
│ 1. API 访问权限 (Casbin RBAC: 角色 → 路径 → 方法) │
│ 2. 菜单/按钮权限 (权限码: crm:customer:create) │
│ 3. 数据范围权限 (data_scope: 全部/本部门/仅本人) │
└─────────────────────────────────────────────────────────┘
一、认证:JWT 双令牌
企智通使用 JWT(JSON Web Token) 作为无状态认证凭证。为兼顾安全与用户体验,采用双令牌机制。
双令牌设计
| 令牌 | 作用 | 有效期 | 存储位置 |
|---|---|---|---|
| access_token | 访问业务 API 的凭证 | 短(如 30 分钟) | 前端内存 / sessionStorage |
| refresh_token | 用于换取新的 access_token | 长(如 7 天) | 前端 localStorage(加密) |
为什么用双令牌
- access_token 短效:即使泄露,攻击窗口很小(30 分钟内)
- refresh_token 长效:用户无需频繁重新登录,体验好
- 可吊销:refresh_token 存储在服务端(Redis),可主动失效(改密、踢下线、注销)
登录流程
用户提交账号 + 密码
│
▼
后端校验:
· 用户名是否存在
· 密码是否正确 (bcrypt 比对)
· 账号是否锁定 / 停用
│
▼
签发 access_token (payload: user_id, roles, exp)
签发 refresh_token (payload: user_id, exp)
│ refresh_token 同时写入 Redis (key: user_id, 可踢出)
▼
返回前端 { accessToken, refreshToken, userInfo, permissions }
令牌刷新流程
前端发起请求 (携带 access_token)
│
▼
后端校验 access_token:
· 过期? → 返回 401 (token expired)
· 有效? → 放行
│
▼
前端拦截器收到 401 (expired)
│
▼
前端用 refresh_token 调用 POST /api/v1/auth/refresh
│
▼
后端校验 refresh_token:
· 是否过期?
· 是否在 Redis 中 (未被吊销)?
│
▼
签发新的 access_token (+ 可能轮换 refresh_token)
│
▼
前端用新 token 重放原请求 (用户无感知)
若 refresh_token 也过期,则前端跳转登录页。
密码安全
- 密码使用 bcrypt 哈希存储(自带盐,抗彩虹表)
- 密码强度策略:最小长度、大小写 + 数字 + 符号组合
- 登录失败锁定:连续失败 N 次锁定账号 M 分钟
- 初始密码 / 重置密码后强制首次登录修改
第三方登录
企智通支持企业微信扫码登录(OAuth2),可作为账号密码登录的补充:
- 配置企业 CorpID、AgentID、Secret、回调地址
- 用户扫码后,后端用企业微信 code 换取用户身份
- 将企业微信 userid 与系统 user 绑定(首次扫码自动创建或关联已有账号)
- 绑定后,扫码即等价于登录,同样签发 JWT 双令牌
二、授权:Casbin RBAC
认证解决「你是谁」,授权解决「你能做什么」。企智通的授权模型基于 Casbin 的 RBAC(基于角色的访问控制)。
权限四要素
用户(user) ──属于──▶ 角色(role) ──拥有──▶ 菜单(menu) ──绑定──▶ 权限码(permission) + API
| 概念 | 说明 | 示例 |
|---|---|---|
| 用户 | 系统账号 | 张三(user_id=10) |
| 角色 | 权限的集合,用户与权限的中介 | 销售经理、财务、HR、超管 |
| 菜单 | 前端导航项,关联一个页面与若干按钮权限码 | 「客户管理」菜单 |
| 权限码 | 细粒度动作标识,按钮级控制 | crm:customer:create、crm:customer:export |
| API | 后端路由(路径 + 方法),菜单注册时声明 | POST /api/v1/crm/customers |
角色 → 菜单 → API → 权限码 的映射
- 管理员在后台配置角色:勾选该角色能访问哪些菜单
- 菜单注册时声明其包含的 API 与权限码(由模块
RegisterMenus()定义) - 用户被赋予一个或多个角色:用户的有效权限 = 所有角色权限的并集
API 级校验(Casbin)
每个请求经过 CasbinRBAC 中间件时:
输入: (当前用户的角色集合, 请求路径, HTTP方法)
│
▼
Casbin 策略匹配:
· 角色 → 菜单 → API 的关联是否包含当前 (路径, 方法)?
│
▼
匹配成功 → 放行
匹配失败 → 403 Forbidden
Casbin 的策略存储在 sys_menus(菜单与其绑定的 API),并同步加载到内存中的 enforcer,匹配是 O(1) 级别的高效查表。
菜单与按钮级权限(前端)
前端拿到当前用户的权限码集合后:
- 菜单渲染:只渲染用户有权限的菜单项(动态路由)
- 按钮控制:用
<Access code="crm:customer:create">包裹按钮,无权限码则不渲染
<Access code="crm:customer:create">
<Button onClick={handleCreate}>新增客户</Button>
</Access>
注意:前端权限控制只是体验优化(不显示无权限的按钮),真正的安全防线在后端 Casbin。即使绕过前端直接调 API,后端也会 403。
三、数据权限(data_scope)
API 权限控制「能不能调用这个接口」,数据权限控制「调用时能看到哪些数据」。同样的「客户列表」接口,销售员只能看自己的客户,销售总监能看整个部门的客户。
数据范围(data_scope)
每个角色配置一个 data_scope,取值:
| data_scope | 含义 | 看到的数据 |
|---|---|---|
| 全部 | 全部数据 | 所有客户 / 订单 |
| 本部门 | 仅本部门 | 创建人部门 = 当前用户部门 |
| 本部门及子部门 | 本部门 + 下级部门 | 创建人部门 ∈ 当前用户部门及子部门 |
| 仅本人 | 仅自己创建 | 创建人 = 当前用户 |
| 自定义 | 指定部门集合 | 创建人部门 ∈ 配置的部门列表 |
实现机制
数据权限在 Service 层注入到查询条件中:
// 伪代码
func (s *CustomerService) List(ctx context.Context, query Query) {
scope := s.getDataScope(ctx.CurrentUser())
// scope 自动追加 WHERE 条件
// 本部门: WHERE owner_dept_id = ?
// 本部门及子: WHERE owner_dept_id IN (?)
// 仅本人: WHERE owner_id = ?
db.Model(&Customer{}).Where(scope).Find(&list)
}
数据权限是横向隔离,与 API 权限(纵向能否访问)正交,两者叠加构成完整的授权。
四、超级管理员旁路
系统预设一个超级管理员角色(通常是 user_id=1 或标记 is_super=1),拥有全部权限且跳过所有校验:
- Casbin 校验:检测到超管直接放行,不查策略
- 数据权限:超管 data_scope 强制为「全部」
- 操作日志:照常记录(超管的操作更需要审计)
- 受限操作:数据重置等高危功能仅超管可见
超管旁路保证了系统永远有一个可用的最高权限账号,避免因权限配置错误导致系统无法管理。但生产环境应严格控制超管账号数量与使用。
安全最佳实践
| 场景 | 建议 |
|---|---|
| 生产部署 | 立即修改默认超管密码,禁用 demo 账号 |
| 令牌存储 | access_token 放内存,避免 localStorage(防 XSS) |
| HTTPS | 生产环境强制 HTTPS,防止令牌被中间人窃取 |
| 操作审计 | 关键操作开启操作日志,定期巡检 |
| 最小权限 | 按岗位精准配置角色,避免赋予「全部数据」范围 |
| 定期巡检 | 检查异常登录、权限变更、超管使用记录 |