feat: 上线前安全加固与发布工程化;appmall 由插件迁移为独立应用

安全(P0)
- 发布包签名:新增 scripts/package_keygen.php、scripts/package_verify.php、
  ywxapp\service\PackageVerifier;FrameworkService::apply() 解压前强制验签
  (ywxapp.framework_package_verify,默认开启)
- JWT 收紧:config/jwt.php 移除公开默认密钥,JwtService::assertStrongSecret()
  校验非空 / 长度≥32 / 已知弱值;.env.production.example 键名由 JWT_SECRET_KEY 修正为 JWT_SECRET
- /api/v1/Article:免登录白名单收窄为只读接口,写操作字段白名单 + assertOwner() 越权校验
- 开发者令牌:查找接口不再回显明文、统一失败文案 + 限流
- TemplateInstaller:文件名白名单 + realpath 归一化 + 目录内断言,修复 Zip Slip;补 use ZipArchive
- 后台登录:接入验证码、失败锁定与登录限流(config/ratelimit.php)
- 缓存 / Session 驱动可由 .env 覆盖(CACHE_DRIVER / SESSION_TYPE),集群部署改 redis
- paycenter 支付日志开关改为 PAY_LOG,不再跟随 APP_DEBUG

工程化 / 交付
- 新增 CHANGELOG.md 与 .env.production.example;composer.lock 入库锁定依赖版本
- 插件离线安装 CLI 增加 --uid,后台上传安装透传 uid,付费插件授权校验可通过
- 授权拒绝文案区分「未携带 uid」与「未找到有效授权」,便于排障
- .gitignore 放行 .env.production.example / .env.addon.example / .gitattributes / public/.htaccess

结构
- appmall 由 addon/appmall 迁移为独立应用 app/appmall(市场 / 开发者中心 / 审核 / 分润)
This commit is contained in:
yangwx committed 2026-10-22 00:53:39 +08:00
1 parent 1d49e6f5ee
commit 18101cf11a
636 files changed
+58517 -9519

No files matched your search

+33 -6
View File
@@ -35,6 +35,14 @@ return [
'login_captcha' => true,
//登录失败超过10次则1天后重试
'login_failure_retry' => true,
// ---- 登录爆破防护(AdminAuth::login 消费;不依赖 RateLimit 中间件 ----
// 原因:config/ratelimit.php 的 RateLimit 中间件只在 app/api 注册,
// 后台 /admin/login 走不到,必须有独立的计数兜底。
// 同一 IP + 同一账号在窗口内失败达上限 → 封禁 block 秒。设 0 表示关闭限流。
'login_fail_limit' => (int) env('LOGIN_FAIL_LIMIT', 10),
'login_fail_window' => (int) env('LOGIN_FAIL_WINDOW', 600),
'login_block_time' => (int) env('LOGIN_BLOCK_TIME', 900),
//是否同一账号同一时间只能在一个地方登录
'login_unique' => false,
//是否开启IP变动检测
@@ -55,6 +63,9 @@ return [
'version' => '1.1.1',
//API接口地址(本地自测:指向本机;生产部署需改回 https://www.ywxapp.cn,并确保中心站已部署含 framework 路由的新版 market)
'api_url' => env('APP_API_URL', 'https://www.ywxapp.cn'),
// 市场角色(控制 is_market_client()):center=本机即中心站 / client=本机即客户机 / auto=按 api_url+本机主机推断
// 显式声明优于 auto:避免 api_url 误指向别处时角色翻转。本地开发建议直接设 center。
'market_mode' => env('APP_MARKET_MODE', 'auto'),
// 是否开启多语言
'lang_switch_on' => true,
//是否允许未知来源的插件压缩包(本地离线安装需开启,配合 APP_DEBUG 跳过远程授权校验)
@@ -65,7 +76,7 @@ return [
'backup_global_files' => true,
//插件纯净模式,插件启用后是否删除插件目录的application、public和assets文件夹
'addon_pure_mode' => true,
'addon_license_check' => false, // 运行期授权校验开关(true 时声明 license=>true 的付费插件需存在有效授权才加载)
'addon_license_check' => env('ADDON_LICENSE_CHECK', false), // 运行期授权校验开关(true 时声明 license=>true 的付费插件需存在有效授权才加载)
// 下载签名密钥:用于 download 接口 generateSign 校验,防 license_key 泄漏后被冒用下载。
// 生产环境务必改为强随机值(建议写入 .env 的 ADDON_SECRET),并保持与 API 客户端一致。
// 生成强随机密钥示例: openssl rand -base64 64 或 bin2hex(random_bytes(32))
@@ -75,15 +86,31 @@ return [
// 自托管市场(中心站 addon/market 插件,路由 /api/addon/*)已配套实现:客户端 AddonService::download() 在开启时自动签名;
// 启用前请确保 api_url 指向本服务器(服务端/客户端共享同一 addon_secret)。
'addon_download_sign' => false,
// 应用市场(appmall)插件的运行期配置(开发者令牌、远程开关、SSL 校验、抽成比例等)
// 已迁移至插件目录 addon/appmall/config/appmall.php(随插件分发、支持 .env 覆盖),
// 通过 config('appmall.*') 读取。
// 主框架发布包(整站包/补丁/安装包)强制验签开关。
// true:FrameworkService::apply() 解压前必须过 PackageVerifier 的 RSA 验签,
// 未签名/签名不符/版本不一致一律拒绝安装(防升级通道被用作远程代码执行)。
// 仅「自建升级服务器 + 自己签名」的内网调试场景可关闭:FRAMEWORK_PACKAGE_VERIFY=false
'framework_package_verify' => env('FRAMEWORK_PACKAGE_VERIFY', true),
// 发布包公钥路径(留空用 ywxapp/data/package_public.pem,或 PackageVerifier::BUILTIN_PUBLIC_KEY)
'package_public_key_path' => env('PACKAGE_PUBLIC_KEY_PATH', ''),
// 应用市场(appmall)的运行期配置(开发者令牌、远程开关、SSL 校验、抽成比例等):
// appmall 已由插件迁为普通应用(app/appmall),其配置统一放在根级 config/appmall.php
// (全局加载,任意应用均可 config('appmall.*')),并由 .env 覆盖。
// 开发模式开关(抄 Discuz! 插件设计器):开启后后台「插件」出现「设计插件」入口,
// 可可视化创建/编辑本地插件(生成骨架、增删配置项/菜单/事件/路由、生成页面模块),改动实时生效。
// 生产环境务必保持 false(与 APP_DEBUG 解耦,独立开关)。
'developer_token' => env('DEVELOPER_TOKEN', ''),
// 历史上后台「发布到官方市场」误把 developer_token 关联到 DEVELOPER_TOKEN,导致用户照着错误提示配错键。
// 现统一为 APPMARKET_DEV_TOKEN(与 config/appmall.php 同源),错误提示也同步修正。
'developer_token' => env('APPMARKET_DEV_TOKEN', ''),
'addon_developer' => env('ADDON_DEVELOPER', false),
// 钩点可视化标注(Discuz 式开发者模式):开启后页面上的每个 hook() 埋点用蓝色虚线框 + 「⟡ 钩子名」标签标出,
// 未挂载插件的空埋点同样显示,便于对照「模板位置 ↔ 钩子名」。
// 后台「数据运维 → 模板钩子」可临时开关(cookie hook_dev,只在后台应用内生效,防泄漏给前台访客);
// 本开关为全局模式,前台/会员中心也需要标注时才置 true(建议仅开发环境)。
'hook_developer' => env('HOOK_DEVELOPER', false),
'backend_login_url' => '/admin/login/index', // 后台登录页地址(app_map: admin => backend)
'member_login_url' => '/user/login/index', // 会员中心登录页地址(app_map: member => frontend)