479 lines
23 KiB
Markdown
479 lines
23 KiB
Markdown
# 4.10 安全架构设计
|
||
|
||
:::tip 概述
|
||
本章详细阐述系统的安全架构设计,围绕认证、授权、防护、审计四大安全领域展开,旨在帮助开发者全面理解系统的安全保障体系。
|
||
:::
|
||
|
||
**认证(Authentication)**:系统采用基于 JWT 的双令牌认证机制,用户通过用户名密码登录后获取 Token,后续请求通过 `Authorization: Bearer <token>` 头传递,由 `Authenticate` 中间件(别名 `auth.jwt`)统一拦截并验证 Token 的有效性(签名校验、有效期检查),签名密钥启动时 fail-close 校验。
|
||
|
||
**授权(Authorization)**:基于 RBAC 模型设计,权限控制粒度细化至接口级。用户通过角色继承权限节点(如 `sys:user:add`),由 `#[Permission]` 注解在控制器层声明,`Authenticate` 中间件反射校验(三级缓存:Redis → Laravel Cache → 数据库)。超级管理员(`uid=1`)自动放行。
|
||
|
||
**防护(Protection)**:系统从多维度构建安全防护体系。参数校验通过 FormRequest 在 HTTP 入口自动拦截非法输入;敏感密码采用 bcrypt 双重加盐哈希存储;演示模式下 `DemoMiddleware` 统一拦截写操作;跨域配置精确控制可信域名;Nginx 仅允许入口 PHP 执行。
|
||
|
||
**审计(Audit)**:系统提供完备的操作审计能力。`LogOperation` 中间件自动记录所有写操作的请求路径、参数(排除敏感字段)、操作人、IP 归属地及执行结果;登录日志独立记录每次登录尝试,便于安全事件追溯。
|
||
|
||
:::tip 安全设计原则
|
||
- **纵深防御**:认证、授权、参数校验多层防护,单一防线失效时仍有其他机制兜底
|
||
- **最小权限**:用户仅能访问已授权接口,角色权限按需分配
|
||
- **数据不落地**:敏感信息(Token、密码)不在日志中明文记录(操作日志 `param` 排除 `password`/`token`),密码哈希不可逆
|
||
- **安全默认**:`JWT_SECRET` 弱密钥拒绝启动(fail-close),生产 `APP_DEBUG=false` 隐藏错误详情
|
||
:::
|
||
|
||
## 安全架构总览
|
||
|
||
```text
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ 安全防御层次 │
|
||
│ │
|
||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||
│ │ 网络层防护 │ │
|
||
│ │ ┌──────────┐ ┌──────────────┐ ┌────────────────────────┐ │ │
|
||
│ │ │ CORS │ │ 上传体积限制 │ │ Nginx 仅入口 index.php │ │ │
|
||
│ │ │ 跨域控制 │ │ DoS 防御 │ │ 可见性攻击防护 │ │ │
|
||
│ │ └──────────┘ └──────────────┘ └────────────────────────┘ │ │
|
||
│ └───────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||
│ │ 认证层 │ │
|
||
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │
|
||
│ │ │ JWT 认证 │ │ 验证码 │ │ 双令牌 │ │ 密钥 fail- │ │ │
|
||
│ │ │ Bearer │ │ 图形验证 │ │ access+ │ │ close 启动 │ │ │
|
||
│ │ │ │ │ + IP限流 │ │ refresh │ │ 校验 │ │ │
|
||
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │ │
|
||
│ └───────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||
│ │ 授权层 │ │
|
||
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ │
|
||
│ │ │ RBAC 权限 │ │ 演示模式保护 │ │ 超级管理员跳过 │ │ │
|
||
│ │ │ #[Permission]│ │ DemoMiddleware│ │ uid=1 bypass │ │ │
|
||
│ │ └──────────────┘ └──────────────┘ └──────────────────┘ │ │
|
||
│ └───────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||
│ │ 数据层 │ │
|
||
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │
|
||
│ │ │ 密码加密 │ │ 参数校验 │ │ SQL 注入 │ │ 操作日志 │ │ │
|
||
│ │ │ 双重加盐 │ │FormRequest│ │ ORM 防护 │ │ 审计追踪 │ │ │
|
||
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │ │
|
||
│ └───────────────────────────────────────────────────────────┘ │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## 一、认证机制
|
||
|
||
### 登录流程
|
||
|
||
```text
|
||
1. 前端提交 { username, password, key, code }
|
||
(验证码参数 key/code 与 /api/captcha 响应字段一致)
|
||
│
|
||
▼
|
||
2. CaptchaService::verify(key, code) ← 验证码校验(配置 CAPTCHA_LENGTH>0 时启用)
|
||
│ 失败 → 返回"验证码错误"
|
||
▼
|
||
3. JwtService::login(username, password) ← 查询用户 + 密码校验
|
||
│ 用户不存在/密码错误 → 统一提示(不暴露账号是否存在)
|
||
▼
|
||
4. password_verify(password + salt, hash) ← bcrypt 双重加盐校验
|
||
│ 不匹配 → 统一提示
|
||
▼
|
||
5. 检查 user.status == 1 ← 账号状态
|
||
│ 非启用 → 返回"账号已被禁用"
|
||
▼
|
||
6. 签发双令牌 + 写登录日志(成功/失败均记录)
|
||
│
|
||
▼
|
||
7. 返回 { access_token, refresh_token, token_type, expires_in, user }
|
||
```
|
||
|
||
### JWT 双令牌认证
|
||
|
||
```text
|
||
[前端] [后端]
|
||
│ │
|
||
│── POST /api/login ──────────→│ (公开接口,中间件组外)
|
||
│ {username, password, │── 验证码校验(key/code)
|
||
│ key, code} │── 密码校验(双重加盐 bcrypt)
|
||
│ │── 签发 access_token + refresh_token
|
||
│←─ {access_token, ─────────────│
|
||
│ refresh_token} │
|
||
│ │
|
||
│── GET /api/xxx ─────────────→│ (认证接口,auth.jwt 组内)
|
||
│ Authorization: │── JwtService::parseToken
|
||
│ Bearer <access_token> │── 校验签名 + 有效期 + type=access
|
||
│ │── 注入 attributes['userInfo']
|
||
│←─ {data} ────────────────────│
|
||
│ │
|
||
│── access_token 过期 ─────────→│
|
||
│── POST /api/refresh ─────────→│ (公开接口)
|
||
│ {refresh_token} │── 校验 refresh 类型 + Redis jti(一次性消费)
|
||
│←─ {新令牌对} ─────────────────│
|
||
```
|
||
|
||
### Token 设计
|
||
|
||
| Token 类型 | 默认有效期 | 用途 | 存储 |
|
||
|-----------|--------|------|------|
|
||
| access_token | 2 小时(`JWT_ACCESS_TTL=7200`) | 接口认证 | 无状态(JWT) |
|
||
| refresh_token | 7 天(`JWT_REFRESH_TTL=604800`) | 刷新令牌 | **Redis 有状态**(支持踢下线、改密全端失效、一次性消费) |
|
||
|
||
### 安全要点
|
||
|
||
1. **密码错误统一提示**:无论用户不存在还是密码错误,统一提示,避免暴露账号是否存在
|
||
2. **账号状态校验**:`status != 1` 时禁止登录
|
||
3. **Token 类型校验**:access 与 refresh 通过 `type` 字段区分,不可混用(refresh 令牌不能访问业务接口)
|
||
4. **密钥 fail-close**:`JWT_SECRET` 为空或不足 32 字符时 `AppServiceProvider` 抛异常**拒绝启动**——弱密钥无法上线
|
||
5. **验证码限流**:按 IP 限流(`CAPTCHA_RATE_WINDOW` / `CAPTCHA_RATE_LIMIT`),万能码仅 `APP_DEBUG=true` 生效
|
||
|
||
## 二、授权机制
|
||
|
||
### RBAC 权限模型(菜单即权限)
|
||
|
||
```text
|
||
┌─────────┐ ┌────────────┐ ┌─────────┐ ┌────────────┐ ┌─────────┐
|
||
│ User │───►│ UserRole │───►│ Role │───►│ RoleMenu │───►│ Menu │
|
||
│ │ │ │ │ │ │ │ │ │
|
||
│ id │ │ user_id │ │ id │ │ role_id │ │ id │
|
||
│ username│ │ role_id │ │ name │ │ menu_id │ │permission│
|
||
└─────────┘ └────────────┘ │ code │ └────────────┘ │ type │
|
||
└─────────┘ └─────────┘
|
||
```
|
||
|
||
- 菜单表的 `permission` 字段即权限编码(`sys:{模块}:{操作}`)
|
||
- `type=1`(菜单)且 `permission` 非空的节点参与接口鉴权
|
||
- 角色-菜单变更时 `RoleMenuObserver` / `MenuObserver` 自动清除权限缓存
|
||
|
||
### 权限校验流程(Authenticate::checkPermission)
|
||
|
||
```php
|
||
// app/Http/Middleware/Authenticate.php(节选)
|
||
|
||
protected function checkPermission(int $uid, string $permissionCode): bool
|
||
{
|
||
// 超级管理员跳过
|
||
if ($uid === 1) {
|
||
return true;
|
||
}
|
||
|
||
// 从 Redis 获取权限(一级缓存)
|
||
try {
|
||
$cached = Redis::get("perms:{$uid}");
|
||
if ($cached) {
|
||
$perms = json_decode($cached, true) ?? [];
|
||
// 拥有通配权限或包含目标编码即通过
|
||
return in_array('*:*:*', $perms) || in_array($permissionCode, $perms);
|
||
}
|
||
} catch (\Exception $e) {
|
||
// Redis 不可用时继续降级
|
||
}
|
||
|
||
// 二级缓存:Laravel Cache(5 分钟)
|
||
$cacheKey = "perms_fallback:{$uid}";
|
||
$permissions = Cache::remember($cacheKey, 300, function () use ($uid) {
|
||
return $this->loadPermissionsFromDb($uid);
|
||
});
|
||
|
||
return in_array('*:*:*', $permissions) || in_array($permissionCode, $permissions);
|
||
}
|
||
|
||
// 数据库加载:user_role → role_menu join menu(未删除、type=1、permission 非空)
|
||
protected function loadPermissionsFromDb(int $uid): array
|
||
{
|
||
$roleIds = UserRole::where('user_id', $uid)->pluck('role_id')->toArray();
|
||
if (empty($roleIds)) {
|
||
return [];
|
||
}
|
||
|
||
return RoleMenu::join('menu', 'role_menu.menu_id', '=', 'menu.id')
|
||
->whereIn('role_menu.role_id', $roleIds)
|
||
->where('menu.is_delete', 0)
|
||
->where('menu.type', 1)
|
||
->where('menu.permission', '!=', '')
|
||
->pluck('menu.permission')
|
||
->toArray();
|
||
}
|
||
```
|
||
|
||
### 数据权限
|
||
|
||
`HasDataScope` 支持三种数据范围(当前 `laravel_role` 表不含 `data_scope` 字段,实际一律按"全部数据"执行):
|
||
|
||
| data_scope | 范围 | 实现(HasDataScope) |
|
||
|-----------|------|------|
|
||
| 1 | 全部数据 | 不过滤 |
|
||
| 2 | 本部门数据 | `WHERE dept_id = 用户部门ID` |
|
||
| 3 | 仅本人数据(默认) | `WHERE create_user = 用户名` |
|
||
|
||
## 三、防护机制
|
||
|
||
### 密码加密(bcrypt 双重加盐)
|
||
|
||
```text
|
||
密码存储流程:
|
||
原始密码 + 外部 salt(随机字符串,存 user.salt 字段)
|
||
│
|
||
▼ password_hash($password . $salt, PASSWORD_BCRYPT, ['cost' => 12])
|
||
│
|
||
▼ bcrypt 内部自动生成 salt 并嵌入哈希
|
||
│
|
||
▼ 存储:password 字段(60 字符哈希)+ salt 字段(独立存储)
|
||
|
||
密码校验流程:
|
||
用户输入密码 + 数据库 salt
|
||
│
|
||
▼ password_verify($input . $salt, $hashed)
|
||
│
|
||
▼ true / false
|
||
```
|
||
|
||
- 成本因子固定为 12(`PasswordService::BCRYPT_ROUNDS`,对应 `.env` 的 `BCRYPT_ROUNDS=12`)
|
||
- **双重加盐**:外部 salt 存独立字段,数据库泄露后仍需同时获取 salt 才能暴力破解;bcrypt 内部 salt 进一步增加彩虹表攻击难度
|
||
|
||
### 参数校验(FormRequest 入口拦截)
|
||
|
||
通过控制器方法类型注入 FormRequest,进入业务逻辑前完成格式校验:
|
||
|
||
```php
|
||
class UserRequest extends BaseRequest
|
||
{
|
||
public function rules(): array
|
||
{
|
||
return [
|
||
'username' => 'required|string|min:3|max:50',
|
||
'realname' => 'required|string|max:50',
|
||
'mobile' => 'nullable|max:20',
|
||
'email' => 'nullable|email',
|
||
];
|
||
}
|
||
}
|
||
```
|
||
|
||
校验失败由 `failedValidation()` 直接返回 `code=1` + 字段错误明细,**不进入控制器方法**;唯一性等业务规则由 Service 层兜底(防止绕过格式校验的组合攻击)。
|
||
|
||
### SQL 注入防护
|
||
|
||
Eloquent 查询构造器使用参数绑定,自动防止 SQL 注入:
|
||
|
||
```php
|
||
// ✅ ORM 查询(安全,自动参数绑定)
|
||
User::where('username', $username)->first();
|
||
// 生成:SELECT * FROM laravel_user WHERE username = ? [$username]
|
||
|
||
// ✅ 原生查询(手动绑定参数)
|
||
DB::select("SELECT * FROM laravel_user WHERE username = ?", [$username]);
|
||
|
||
// ❌ 危险!字符串拼接(禁止)
|
||
DB::select("SELECT * FROM laravel_user WHERE username = '$username'");
|
||
```
|
||
|
||
查询排序也受白名单保护:前端 `orderField` 必须命中 `pageOrderBy` 与 `orderFieldAllow` 的并集,否则忽略(防排序注入)。
|
||
|
||
:::warning 安全红线
|
||
永远不要将用户输入直接拼接到 SQL 字符串中。即使使用原生 SQL,也必须使用参数绑定(`?` 占位符)。
|
||
:::
|
||
|
||
### 文件上传安全
|
||
|
||
```text
|
||
上传文件安全检查链:
|
||
│
|
||
▼ 1. 文件大小限制(config/file.php → FILE_MAX_SIZE,默认 10MB)
|
||
│
|
||
▼ 2. 文件扩展名白名单(FILE_IMAGE_EXT / FILE_EXT)
|
||
│ (svg 已移除:内嵌 JS 导致 XSS 风险)
|
||
│
|
||
▼ 3. MIME 类型真实检测(fileinfo 扩展,_import 等场景强制校验)
|
||
│
|
||
▼ 4. 存储路径由服务端生成(防止路径遍历)
|
||
│
|
||
▼ 5. Nginx 仅允许入口 index.php 执行 —— 上传目录中的 .php 一律 404
|
||
```
|
||
|
||
```nginx
|
||
# docker/nginx.conf:只有入口可执行,其余 .php 一律拒绝
|
||
location = /index.php { /* FastCGI 转发 */ }
|
||
location ~ \.php$ {
|
||
return 404;
|
||
}
|
||
```
|
||
|
||
### XSS 防护
|
||
|
||
```text
|
||
防护措施:
|
||
1. 前端:Vue 模板插值默认转义({{ }}),避免注入
|
||
2. 后端:响应强制 Content-Type: application/json(ForceJsonResponse 中间件),
|
||
浏览器不会按 HTML 渲染
|
||
3. 富文本:字段独立处理链(contentFields),内容过滤与占位符机制
|
||
4. 上传白名单移除 svg(内嵌脚本风险)
|
||
```
|
||
|
||
### 演示模式保护
|
||
|
||
```php
|
||
// DemoMiddleware 拦截写操作
|
||
// .env: DEMO_MODE=true(读取 config('app.demo_mode'))
|
||
|
||
// 受保护:POST/PUT/DELETE/PATCH 请求 → 返回 403
|
||
// 放行:GET 请求 + 标注 #[DemoAllow] 注解的方法
|
||
```
|
||
|
||
```php
|
||
// 标注 #[DemoAllow] 的方法在演示模式下仍可执行
|
||
#[DemoAllow]
|
||
public function login(LoginRequest $request): JsonResponse { ... }
|
||
```
|
||
|
||
### CORS 跨域控制
|
||
|
||
```ini
|
||
# .env
|
||
# 开发环境:允许所有(仅限开发)
|
||
CORS_ALLOW_ORIGIN=*
|
||
|
||
# 生产环境:限制具体域名(多个来源用逗号分隔)
|
||
CORS_ALLOW_ORIGIN=https://admin.example.com
|
||
```
|
||
|
||
```php
|
||
// Cors 中间件处理逻辑(见 4.2 请求处理链路):
|
||
// 1. OPTIONS 预检请求 → 直接返回 204 + CORS 头(不进业务逻辑)
|
||
// 2. 凭证模式下不能用 * → 回显具体 Origin + Vary: Origin(防 CDN 缓存串扰)
|
||
// 3. 仅 Origin 被允许时才设置其余 CORS 头
|
||
```
|
||
|
||
## 四、审计机制
|
||
|
||
### 操作日志(laravel_operation_log)
|
||
|
||
`LogOperation` 中间件基于 `#[OperationLog]` 注解自动记录:
|
||
|
||
```php
|
||
#[OperationLog('用户管理-新增', OperationLog::TYPE_ADD, '新增用户:{username}')]
|
||
public function add(UserRequest $request): JsonResponse { ... }
|
||
```
|
||
|
||
**记录字段:**
|
||
|
||
| 字段 | 说明 | 示例 |
|
||
|------|------|------|
|
||
| `title` | 操作标题(来自注解,无注解回退请求路径) | 用户管理-新增 |
|
||
| `type` | 操作类型(30 种常量,无注解按 HTTP 方法推断) | 1=新增 2=修改 3=删除 4=查询 5=导入 6=导出 |
|
||
| `method` | HTTP 方法 | POST |
|
||
| `request_method` | 控制器方法名 | add |
|
||
| `url` | 请求路径 | /api/user/add |
|
||
| `param` | 请求参数(JSON,**排除 password/token**,截断 2000 字) | {"username":"admin"} |
|
||
| `result` | 响应内容(截断 2000 字) | {"code":0,...} |
|
||
| `ip` / `location` | 客户端 IP / 归属地(ip2region) | 192.168.1.100 / 江苏省南京市 |
|
||
| `os` / `browser` | 操作系统 / 浏览器 | Windows 11 / Chrome 128 |
|
||
| `create_user` | 操作人(userInfo.username) | admin |
|
||
| `status` | 执行状态(code=0/200 → 0,其余 → 1) | 0=成功 1=失败 |
|
||
| `error` | 错误信息(失败时取 msg) | 名称已存在 |
|
||
| `consume_time` | 执行耗时(ms) | 125 |
|
||
|
||
**记录规则:**
|
||
|
||
- 写操作(POST/PUT/DELETE/PATCH)始终记录
|
||
- GET 仅在注解类型为 `EXPORT / DOWNLOAD / IMPORT` 时记录
|
||
- `handle` 阶段采集、`terminate` 阶段落库(响应后执行,不阻塞业务;写入失败静默)
|
||
|
||
### 登录日志(laravel_login_log)
|
||
|
||
```php
|
||
// AuthController::writeLoginLog(节选)—— 成功与失败均记录
|
||
$data = [
|
||
'username' => $username,
|
||
'log_type' => 1, // 1=登录 2=登出
|
||
'source' => 0, // 来源端:0=后台
|
||
'method' => $request->method(),
|
||
'request_method' => $entry, // login / refresh 等入口
|
||
'url' => $request->fullUrl(),
|
||
'param' => json_encode(['username' => $username], ...), // 只记录用户名
|
||
'result' => json_encode(['code' => $success ? 0 : 1, 'msg' => $message], ...),
|
||
'ip' => $requestInfo->getIp(),
|
||
'location' => $requestInfo->getIpLocation(),
|
||
// + os / browser / 耗时
|
||
];
|
||
```
|
||
|
||
**记录字段:**
|
||
|
||
| 字段 | 说明 |
|
||
|------|------|
|
||
| `username` | 登录用户名(无论成功失败都记录) |
|
||
| `log_type` | 1=登录 2=登出 |
|
||
| `status` / `result` | 0=成功 1=失败 + 结果消息 |
|
||
| `ip` / `location` | 客户端 IP / 归属地 |
|
||
| `os` / `browser` | 操作系统 / 浏览器 |
|
||
|
||
### 日志安全要点
|
||
|
||
| 要点 | 说明 |
|
||
|------|------|
|
||
| 密码与令牌不记录 | 操作日志 `param` 排除 `password`、`token`;登录日志只记录用户名 |
|
||
| 参数截断 | `param` / `result` 截断 2000 字,防止超长内容撑爆字段 |
|
||
| 独立存储 | 操作日志和登录日志分别存储在不同表 |
|
||
| 失败不阻断 | 日志写入异常静默处理,不影响主业务 |
|
||
|
||
## 五、安全配置清单
|
||
|
||
### 环境变量(.env)
|
||
|
||
| 配置项 | 开发环境 | 生产环境 | 说明 |
|
||
|--------|---------|---------|------|
|
||
| `APP_DEBUG` | `true` | **`false`** | 关闭调试模式,异常不返回真实消息 |
|
||
| `JWT_SECRET` | 强随机 | **强随机(≥32 字符)** | 不足 32 字符**拒绝启动**(fail-close) |
|
||
| `CORS_ALLOW_ORIGIN` | `*` | **指定域名** | 如 `https://admin.example.com` |
|
||
| `DEMO_MODE` | `false` | `false` | 演示环境按需开启 |
|
||
| `FILE_MAX_SIZE` | 10 | 合理限制 | 防止大文件 DoS |
|
||
| `CAPTCHA_BYPASS_CODE` | 可设 | **必须留空** | 万能验证码仅 debug 生效,生产留空 |
|
||
| `TRUSTED_PROXIES` | 空 | CDN/负载均衡地址 | 防止伪造代理头获取错误 IP |
|
||
| `BCRYPT_ROUNDS` | 12 | 12+ | 密码哈希成本因子 |
|
||
| `API_CAMEL_SNAKE_CONVERT` | `true` | `true` | 驼峰转换开关 |
|
||
|
||
### Nginx 配置
|
||
|
||
```nginx
|
||
# 1. 启用 HTTPS(生产必须)
|
||
server {
|
||
listen 443 ssl;
|
||
ssl_certificate /etc/ssl/cert.pem;
|
||
ssl_certificate_key /etc/ssl/key.pem;
|
||
}
|
||
|
||
# 2. 仅入口 PHP 可执行(docker/nginx.conf 已内置)
|
||
location = /index.php { /* FastCGI 转发 */ }
|
||
location ~ \.php$ { return 404; }
|
||
|
||
# 3. 禁止访问隐藏文件(.env / .git 等,docker/nginx.conf 已内置)
|
||
location ~ /\. { deny all; }
|
||
|
||
# 4. 安全响应头(按需追加)
|
||
add_header X-Content-Type-Options nosniff;
|
||
add_header X-Frame-Options DENY;
|
||
add_header Referrer-Policy strict-origin-when-cross-origin;
|
||
```
|
||
|
||
### 安全检查清单
|
||
|
||
```text
|
||
上线前逐项检查:
|
||
|
||
□ .env 中 APP_DEBUG = false
|
||
□ .env 中 JWT_SECRET 已更换为 ≥32 字符强随机密钥(否则应用拒绝启动)
|
||
□ .env 中 CORS_ALLOW_ORIGIN 指定具体域名
|
||
□ .env 中 CAPTCHA_BYPASS_CODE 留空
|
||
□ Nginx 已配置 HTTPS
|
||
□ Nginx 确认仅入口 index.php 可执行、隐藏文件已拒绝
|
||
□ 数据库密码为强密码,Redis 已设置 requirepass 并同步 REDIS_PASSWORD
|
||
□ 默认管理员密码已修改(admin / 123456)
|
||
□ 演示模式已关闭(除非需要)
|
||
□ 后端 /api/* 路由确认在认证中间件组内(公开接口仅 login/refresh/captcha/oauth2)
|
||
□ 日志中不包含密码等敏感信息
|
||
```
|
||
|
||
## 总结
|
||
|
||
系统通过认证、授权、防护、审计四层安全架构,实现了从网络层到数据层的纵深防御。JWT 双令牌 + Redis 有状态保证认证安全且支持会话管控,RBAC 权限控制细化到按钮级(三级缓存保证性能),bcrypt 双重加盐保护密码安全,操作日志与登录日志实现全链路审计。各层安全机制相互独立又协同配合,确保系统在企业级应用场景下的安全可靠性。
|