认证授权
本系统采用 JWT (JSON Web Token) 进行身份认证,结合 Casbin 实现基于角色的访问控制 (RBAC)。
认证流程
1. 用户登录
用户通过用户名/邮箱和密码进行登录:
bash
POST /api/v1/auth/login请求体:
json
{
"username": "admin",
"password": "password123"
}响应:
json
{
"code": 200,
"message": "登录成功",
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"roles": ["admin"]
}
}
}2. 使用 Token
在后续请求中,需要在 HTTP Header 中携带 token:
bash
Authorization: Bearer <your-jwt-token>3. Token 刷新
当前 token 有效期为 24 小时,过期后需要重新登录获取新 token。
权限系统
角色类型
- admin: 系统管理员,拥有所有权限
- hr: 人力资源管理员,拥有员工管理权限
- manager: 部门经理,拥有部门内管理权限
- employee: 普通员工,拥有基础功能权限
权限分类
用户权限
user:read: 查看用户信息user:write: 修改用户信息user:delete: 删除用户
员工权限
employee:read: 查看员工信息employee:write: 修改员工信息employee:delete: 删除员工employee:create: 创建员工
薪资权限
salary:read: 查看薪资信息salary:write: 修改薪资信息salary:approve: 薪资审批
考勤权限
attendance:read: 查看考勤记录attendance:write: 修改考勤记录attendance:manage: 考勤管理
权限检查流程
- 认证检查: 验证 JWT token 有效性
- 角色检查: 验证用户是否具备相应角色
- 权限检查: 验证用户是否具备具体操作权限
API 权限要求
公开接口(无需认证)
bash
GET /api/v1/ping
GET /api/v1/companies/:id
GET /api/v1/public/contacts
POST /api/v1/auth/register
POST /api/v1/auth/login
POST /api/v1/companies/join-qr/resolve需要认证的接口
大部分接口都需要用户登录后才能访问,需要在请求头中携带有效的 JWT token。
需要管理员权限的接口
所有 /api/v1/admin/* 路径下的接口都需要管理员权限:
bash
# 用户管理
GET /api/v1/admin/users
PUT /api/v1/admin/users/:id/status
# 部门管理
POST /api/v1/admin/departments
GET /api/v1/admin/departments
PUT /api/v1/admin/departments/:id
DELETE /api/v1/admin/departments/:id
# 员工管理
POST /api/v1/admin/employees
GET /api/v1/admin/employees
PUT /api/v1/admin/employees/:id
DELETE /api/v1/admin/employees/:id
# 薪资管理
POST /api/v1/admin/salaries
GET /api/v1/admin/salaries
PUT /api/v1/admin/salaries/:id
DELETE /api/v1/admin/salaries/:id中间件
AuthMiddleware
认证中间件,用于验证 JWT token:
go
// 检查 Authorization: Bearer <token>
// 解析 token 并将用户信息存入上下文AdminMiddleware
管理员权限中间件,用于验证用户是否具有管理员权限:
go
// 检查用户角色是否包含 "admin"
// 如果不是管理员,返回 403 ForbiddenEmployeeContextMiddleware
员工上下文中间件,用于获取当前登录员工的信息:
go
// 根据用户 ID 获取关联的员工信息
// 将员工信息存入上下文供后续处理使用错误处理
认证错误
json
{
"code": 401,
"message": "未认证",
"error": "Invalid or expired token"
}权限错误
json
{
"code": 403,
"message": "无权限",
"error": "Insufficient permissions to access this resource"
}最佳实践
1. Token 安全
- 不要在客户端长期存储 token
- 使用 HTTPS 传输 token
- 定期更换 token
- 避免在 URL 中传递 token
2. 权限设计
- 遵循最小权限原则
- 定期审查用户权限
- 使用角色而不是直接分配权限
- 记录权限操作日志
3. 错误处理
- 不要泄露具体的错误信息
- 统一的错误响应格式
- 记录认证失败的尝试
- 提供清晰的错误提示
示例代码
JavaScript/Node.js
javascript
// 登录获取 token
const login = async (username, password) => {
const response = await fetch('/api/v1/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ username, password })
});
const data = await response.json();
if (data.code === 200) {
localStorage.setItem('token', data.data.token);
return data.data.user;
}
throw new Error(data.message);
};
// 带认证的请求
const authenticatedRequest = async (url, options = {}) => {
const token = localStorage.getItem('token');
return fetch(url, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${token}`
}
});
};Go
go
// 登录
type LoginRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
func Login(username, password string) (string, error) {
req := LoginRequest{Username: username, Password: password}
var resp struct {
Code int `json:"code"`
Data struct {
Token string `json:"token"`
} `json:"data"`
}
// 发送登录请求
// 处理响应...
return resp.Data.Token, nil
}
// 带认证的请求
func AuthenticatedRequest(url string, token string) (*http.Response, error) {
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Authorization", "Bearer "+token)
return http.DefaultClient.Do(req)
}故障排除
Token 无效
- 检查 token 是否过期
- 验证 token 格式是否正确
- 确认 JWT_SECRET 配置正确
权限不足
- 检查用户角色是否正确
- 验证权限配置是否生效
- 确认 RBAC 策略是否正确加载
中间件不生效
- 检查中间件注册顺序
- 验证路由配置是否正确
- 确认中间件是否正确实现