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:
1 parent
1d49e6f5ee
commit
18101cf11a
636 files changed
+58517
-9519
No files matched your search
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | YwxApp [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2026-2036 http://ywxapp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: ywxapp<admin@ywxapp.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
// | 应用市场(appmall)插件【运行期配置的规范默认源】
|
||||
// |
|
||||
// | 本文件是 config('appmall.*') 的完整默认集合(代码级默认值,支持 .env 覆盖)。
|
||||
// | 后台「插件配置」表单(config.php) 仅负责「可编辑字段 + 默认值展示」,
|
||||
// | 数据库保存值按最高优先级覆盖本文件。读取一律用 config('appmall.xxx')。
|
||||
// |
|
||||
// | 约定:布尔项统一用 bool(true/false),不要写 '1'/'0'(字符串),
|
||||
// | 以免代码 if(!$v) 判定出错('0' 在 PHP 里是 truthy)。
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
// ===== 市场基础设置(仅中心站相关:运营与开发者自助)=====
|
||||
'appmall_name' => 'YwxApp 应用市场',
|
||||
'allow_register' => true, // 开放开发者自助注册
|
||||
'need_approval' => true, // 注册后需审核(审核通过才下发开发者令牌)
|
||||
'allow_upload' => true, // 允许后台上传发布核心包
|
||||
|
||||
// ===== 官方市场发布(把本机插件推送到 www.ywxapp.cn 官方公共市场)=====
|
||||
// 与「远程中心站」是两回事:这里是【主动推送】,下面是【被动拉取】。
|
||||
'developer_token' => env('APPMARKET_DEV_TOKEN', ''),
|
||||
'developer_name' => env('APPMARKET_DEV_NAME', ''),
|
||||
|
||||
// ===== 远程市场 / 中心站客户机模式(仅当 ywxapp.api_url 指向其他服务器时生效)=====
|
||||
// 「谁是中心站」由 config/ywxapp.php 的 api_url 决定(留空=本机即中心站,无需下面这些)。
|
||||
'remote_token' => env('APPMARKET_REMOTE_TOKEN', ''), // 客户机↔中心站共享密钥,须与中心站一致
|
||||
'ssl_verify' => env('APPMARKET_SSL_VERIFY', true), // 连接远程市场时 SSL 证书校验
|
||||
|
||||
// ===== 交易 / 安全 =====
|
||||
'commission_rate' => env('APPMARKET_COMMISSION_RATE', 0.2), // 平台抽成 0~1
|
||||
'security_scan' => env('APPMARKET_SECURITY_SCAN', true), // 发布前 PHP 语法+高危函数扫描
|
||||
];
|
||||
+2
-2
@@ -9,8 +9,8 @@
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
// 默认缓存驱动
|
||||
'default' => 'file',
|
||||
// 默认缓存驱动(集群/多副本部署请在 .env 设 CACHE_DRIVER=redis)
|
||||
'default' => env('CACHE_DRIVER', 'file'),
|
||||
'fail_delete' => true,
|
||||
// 缓存连接方式配置
|
||||
'stores' => [
|
||||
|
||||
+9
-1
@@ -7,7 +7,15 @@
|
||||
// | Author: ywxapp<admin@ywxapp.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
return [
|
||||
// 指令定义
|
||||
// 指令定义:这里显式登记,避免依赖「某应用 command 目录是否被自动扫描」
|
||||
//(本仓库为多应用 + 插件式命令目录,自动发现不稳定)。
|
||||
'commands' => [
|
||||
// 应用市场到期/试用到期提醒(appmall 模块随插件分发,类不存在时自动跳过)
|
||||
'appmall:license-notify' => \app\appmall\command\LicenseNotify::class,
|
||||
// 支付配置自检(SDK / mode / 证书 / 沙箱连通性)
|
||||
// 已迁到支付中心插件 addon/paycenter(命令名 paycenter:check);这里保留旧别名,
|
||||
// 插件未安装时类名不存在,Console::addCommands() 会自动跳过,不会致命。
|
||||
'appmall:pay-check' => \addon\paycenter\command\Check::class,
|
||||
'paycenter:check' => \addon\paycenter\command\Check::class,
|
||||
],
|
||||
];
|
||||
+6
-6
@@ -13,12 +13,12 @@ return [
|
||||
'path' => '/',
|
||||
// cookie 有效域名
|
||||
'domain' => '',
|
||||
// cookie 启用安全传输
|
||||
'secure' => false,
|
||||
// httponly设置
|
||||
'httponly' => false,
|
||||
// cookie 启用安全传输(生产 HTTPS 站点在 .env 设 COOKIE_SECURE=true)
|
||||
'secure' => env('COOKIE_SECURE', false),
|
||||
// httponly设置(防 XSS 窃取;JS 侧自行写入的 cookie 不受影响)
|
||||
'httponly' => env('COOKIE_HTTPONLY', true),
|
||||
// 是否使用 setcookie
|
||||
'setcookie' => true,
|
||||
// samesite 设置,支持 'strict' 'lax'
|
||||
'samesite' => '',
|
||||
// samesite 设置,支持 'strict' 'lax'(生产 HTTPS 建议 COOKIE_SAMESITE=strict)
|
||||
'samesite' => env('COOKIE_SAMESITE', 'lax'),
|
||||
];
|
||||
+57
-8
@@ -6,14 +6,63 @@
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: ywxapp<admin@ywxapp.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
// 分层跨域配置。本文件被 **两套** CORS 机制读取,改动前务必读完注释:
|
||||
//
|
||||
// A. ywxapp\middleware\CrossDomain(本项目自研,真正的执行者)
|
||||
// → 读 app_cross(按应用名分层白名单)+ allowed_methods/headers/max_age/credentials
|
||||
// B. think\cors\HandleCors(topthink/think-cors,经服务发现自动挂载,见 vendor/services.php)
|
||||
// → 读 paths / allowed_origins / allowed_methods / allowed_headers / exposed_headers ...
|
||||
// 注意:它的构造函数会对 allowed_methods / allowed_headers 执行 array_map,
|
||||
// 因此这两项**必须是数组**;写成 'GET, POST' 这样的字符串会在每个请求
|
||||
// 构造函数里抛 TypeError(array_map(): Argument #2 must be of type array, string given)。
|
||||
//
|
||||
// 现状:跨域已由 CrossDomain 分层接管,think-cors 保持「空转」(paths 为空 → 不匹配任何路径)。
|
||||
// 若将来想让 think-cors 真正接管,把 paths 配上路径即可,但**必须先移除 CrossDomain**,
|
||||
// 否则两条链路会重复输出 CORS 头。
|
||||
//
|
||||
// app_cross 按应用名取规则,取值:
|
||||
// '*' 全放开(无凭证模式,公开 API 可用)
|
||||
// 'a.com,b.com' 精确白名单(支持凭证)
|
||||
// '*.example' 通配子域
|
||||
// '' 禁止跨域(默认;同源页面不受影响)
|
||||
//
|
||||
// 安全策略:backend / member / frontend 一律留空(零跨域),
|
||||
// 仅 api 对外开放;生产建议把 api 也收紧为精确白名单。
|
||||
return [
|
||||
'paths' => [],
|
||||
'allowed_origins' => ['*'],
|
||||
'app_cross' => [
|
||||
'api' => env('CORS_API_ORIGINS', '*'),
|
||||
'backend' => env('CORS_BACKEND_ORIGINS', ''),
|
||||
'member' => env('CORS_MEMBER_ORIGINS', ''),
|
||||
'frontend' => env('CORS_FRONTEND_ORIGINS', ''),
|
||||
// 未列出的应用(含 addon 应用、index 兜底)默认禁跨域
|
||||
'default' => env('CORS_ORIGINS', ''),
|
||||
],
|
||||
|
||||
// 预检/实际请求允许的方法(**数组**,think-cors 强制要求)
|
||||
'allowed_methods' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
|
||||
|
||||
// 允许的请求头(**数组**,含本框架 JWT 鉴权实际使用的 Authorization / Authorization-Refresh)
|
||||
'allowed_headers' => [
|
||||
'Content-Type',
|
||||
'Authorization',
|
||||
'Authorization-Refresh',
|
||||
'X-Requested-With',
|
||||
'X-CSRF-TOKEN',
|
||||
'token',
|
||||
'X-Token',
|
||||
],
|
||||
|
||||
// 预检结果缓存(秒)
|
||||
'max_age' => 86400,
|
||||
|
||||
// 白名单模式下允许携带 Cookie 凭证(对 '*' 无效,浏览器强制忽略)
|
||||
'supports_credentials' => true,
|
||||
|
||||
// ==================== 以下为 think-cors 专用,保持空转 ====================
|
||||
// 空数组 → HandleCors::hasMatchingPath() 恒 false → 直接放行,不再输出任何 CORS 头。
|
||||
// 不要配成 'api/*' 之类,否则与 CrossDomain 重复输出。
|
||||
'paths' => [],
|
||||
'allowed_origins' => [],
|
||||
'allowed_origins_patterns' => [],
|
||||
'allowed_methods' => ['*'],
|
||||
'allowed_headers' => ['*'],
|
||||
'exposed_headers' => [],
|
||||
'max_age' => 0,
|
||||
'supports_credentials' => false,
|
||||
'exposed_headers' => [],
|
||||
];
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | YwxApp [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2026-2036 http://ywxapp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: ywxapp<admin@ywxapp.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
// CSRF 防护配置(ywxapp\middleware\VerifyCsrfToken 消费,
|
||||
// 注册于 app/{backend,member,frontend}/middleware.php、
|
||||
// addon/docs/middleware.php 与 addon/articles/middleware.php)。
|
||||
return [
|
||||
// 启用 CSRF 校验的应用(会话型);api 走 JWT 不注册中间件
|
||||
// 'docs' / 'articles' / 'blog' 由各自插件的 middleware.php 注册 VerifyCsrfToken 后启用同源校验
|
||||
'session_apps' => ['backend', 'member', 'frontend', 'index', 'docs', 'articles', 'blog'],
|
||||
|
||||
// strict 模式:强校验 __token__ / X-CSRF-TOKEN 与 Session 令牌一致。
|
||||
// 开启前提:所有表单模板注入 {:token_field()}(或隐藏域 __token__)、
|
||||
// JS 请求携带 X-CSRF-TOKEN 头,否则现有表单会被拒绝。默认关闭,
|
||||
// 由第一层「同源校验」先行防护。
|
||||
'strict' => env('CSRF_STRICT', false),
|
||||
|
||||
// 豁免路径(正则,匹配当前应用内 pathinfo):
|
||||
// 支付异步回调、第三方 webhook 等「必然跨源」的 POST 不做同源校验。
|
||||
'exempt' => [
|
||||
'#^(pay|payment|notify|callback|webhook)/#i',
|
||||
'#/(notify|callback|webhook)$#i',
|
||||
],
|
||||
];
|
||||
+7
-4
@@ -13,7 +13,10 @@ return [
|
||||
'algorithm' => 'HS256',
|
||||
|
||||
// 对称密钥(HS256 使用)
|
||||
'secret' => env('JWT_SECRET', 'your-default-secret-key-change-in-production'),
|
||||
// 安全基线:**不允许**默认弱密钥。未配置 JWT_SECRET 时 JwtService 会拒绝签发,
|
||||
// 而不是退回一个公开的兜底值(否则任何人都能伪造管理员 token)。
|
||||
// 生成:openssl rand -base64 64
|
||||
'secret' => env('JWT_SECRET', ''),
|
||||
|
||||
// 非对称密钥路径(RS256 使用)
|
||||
'private_key_path' => root_path() . 'keys/private.key',
|
||||
@@ -23,7 +26,7 @@ return [
|
||||
'access_ttl' => 1800, // Access Token 有效期(秒)→ 30分钟
|
||||
'refresh_ttl' => 604800, // Refresh Token 有效期(秒)→ 7天
|
||||
|
||||
// JWT 标准声明
|
||||
'issuer' => 'https://your-app.com',
|
||||
'audience' => 'https://api.your-app.com',
|
||||
// JWT 标准声明(生产部署请按实际域名覆盖 JWT_ISSUER / JWT_AUDIENCE)
|
||||
'issuer' => env('JWT_ISSUER', 'https://www.ywxapp.cn'),
|
||||
'audience' => env('JWT_AUDIENCE', 'https://www.ywxapp.cn'),
|
||||
];
|
||||
+3
-2
@@ -40,8 +40,9 @@ return [
|
||||
'close' => false,
|
||||
// 日志输出格式化
|
||||
'format' => '[%s][%s] %s',
|
||||
// 是否实时写入
|
||||
'realtime_write' => false,
|
||||
// 是否实时写入:支付/订单/登录失败/授权吊销等关键事件依赖日志,
|
||||
// 缓冲在内存会在进程崩溃时丢失,生产必须实时落盘
|
||||
'realtime_write' => true,
|
||||
],
|
||||
// 其它日志通道配置
|
||||
],
|
||||
|
||||
+92
-19
@@ -8,29 +8,102 @@
|
||||
// | Author: ywxapp<admin@ywxapp.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
// 模拟支付开关:无商户号时直接完成下单并签发授权,便于本地/自托管环境跑通购买闭环。
|
||||
// 生产环境务必在 .env 设置 PAY_MOCK=false 关闭,否则任何人可「免费」购得付费插件。
|
||||
'mock_enable' => env('PAY_MOCK', true),
|
||||
// 应用私钥兼容处理:
|
||||
// .env 只能放单行,私钥通常以「裸 base64」贴进来(带 PEM 头尾的多行内容塞不进 .env)。
|
||||
// SDK 的 get_private_cert()(vendor/yansongda/pay/src/Functions.php)规则很挑食:
|
||||
// ① 是文件路径 → 读文件内容(内容仍要符合 ②③)
|
||||
// ② 以 '-----BEGIN PRIVATE KEY-----' 开头 → 原样返回(必须是真正的 PKCS8)
|
||||
// ③ 其余一律再包一层 '-----BEGIN RSA PRIVATE KEY-----'(按 PKCS1 处理)
|
||||
// 由此得出:PKCS8 必须自己拼好 PKCS8 的 PEM 头;PKCS1 反而必须交【裸 base64】让 SDK 去包。
|
||||
// 反过来做(给 PKCS1 套 'PRIVATE KEY' 头 / 给已是 PEM 的内容再套一层 RSA 头)就会:
|
||||
// openssl_sign(): Supplied key param cannot be coerced into a private key
|
||||
// 所以这里按 DER 里是否含 rsaEncryption 的 OID 判定 PKCS8/PKCS1,再交给 SDK 正确的一侧。
|
||||
$alipaySecretCert = (string) env('PAY_ALIPAY_APP_SECRET_CERT', '');
|
||||
if ($alipaySecretCert !== '') {
|
||||
if (is_file($alipaySecretCert)) {
|
||||
$alipaySecretCert = (string) file_get_contents($alipaySecretCert);
|
||||
}
|
||||
$privateKeyBody = (string) preg_replace('/-----[^-]+-----|\s+/', '', $alipaySecretCert);
|
||||
$der = base64_decode($privateKeyBody, true) ?: '';
|
||||
// rsaEncryption OID:1.2.840.113549.1.1.1 —— 只出现在 PKCS8 的结构里
|
||||
$isPkcs8 = str_contains(substr($der, 0, 64), "\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01");
|
||||
$wrapped = wordwrap($privateKeyBody, 64, "\n", true);
|
||||
$alipaySecretCert = $isPkcs8
|
||||
? "-----BEGIN PRIVATE KEY-----\n{$wrapped}\n-----END PRIVATE KEY-----"
|
||||
: $privateKeyBody;
|
||||
}
|
||||
|
||||
// 支付宝
|
||||
// 证书路径统一按项目根目录解析成绝对路径:
|
||||
// SDK 的 get_public_cert() 是 `is_file($key) ? file_get_contents($key) : $key`
|
||||
// (见 vendor/yansongda/pay/src/Functions.php),相对路径按【进程 CWD】解析:
|
||||
// CLI 在项目根目录 → 能读到;Web 入口是 public/index.php、CWD 变成 public/ → 读不到,
|
||||
// 下单会抛「配置异常: 解析 `app_public_cert_path` 失败」,回调验签同样失败。
|
||||
// 所以在配置层补全一次,业务代码(MarketService / Market / PayCheck)都无需各自处理。
|
||||
$resolveCertPath = static function (string $path): string {
|
||||
if ($path === '') {
|
||||
return '';
|
||||
}
|
||||
if (str_starts_with($path, '/') || str_starts_with($path, '\\') || preg_match('/^[A-Za-z]:[\\\\\/]/', $path)) {
|
||||
return $path;
|
||||
}
|
||||
$root = function_exists('root_path') ? root_path() : dirname(__DIR__) . DIRECTORY_SEPARATOR;
|
||||
|
||||
return $root . ltrim($path, '/\\');
|
||||
};
|
||||
|
||||
return [
|
||||
// 模拟支付开关:仅在 APP_DEBUG=true 且无商户号时生效,便于本地/自托管环境跑通购买闭环。
|
||||
// 默认 false(防御性默认,避免默认开启后被人"免费"购得付费插件授权)。
|
||||
// 三重防御确保生产安全:
|
||||
// 1) 本开关默认关闭(防止默认值泄露);
|
||||
// 2) 安装向导 public/install/Installer.php::writeEnv 会强制把 PAY_MOCK=false 写入 .env;
|
||||
// 3) 即使 .env 被手误改错,MarketService / Market 在生产(APP_DEBUG=false)也会拒绝执行 mock。
|
||||
// 本地调试:.env 中显式设置 PAY_MOCK=true(与 APP_DEBUG=true 配对使用)。
|
||||
'mock_enable' => env('PAY_MOCK', false),
|
||||
|
||||
// 支付宝(Yansongda Pay v3.7 —— 仅支持「证书模式」,普通公钥模式不再被识别)。
|
||||
// 证书在开放平台 → 应用 → 接口加签方式(沙箱同理)下载:
|
||||
// app_public_cert_path → appCertPublicKey_*.crt(应用公钥证书)
|
||||
// alipay_public_cert_path → alipayCertPublicKey_*.crt(支付宝公钥证书,验签用)
|
||||
// alipay_root_cert_path → alipayRootCert.crt(支付宝根证书)
|
||||
// 缺任一项都会抛「配置异常: 缺少支付宝配置 -- [xxx]」,见
|
||||
// vendor/yansongda/pay/src/Plugin/Alipay/V2/StartPlugin.php
|
||||
// vendor/yansongda/pay/src/Plugin/Alipay/V2/AddPayloadSignaturePlugin.php
|
||||
'alipay' => [
|
||||
'mode' => env('PAY_ALIPAY_MODE', 'normal'), // normal | dev | query
|
||||
'app_id' => env('PAY_ALIPAY_APP_ID', ''),
|
||||
'ali_public_key' => env('PAY_ALIPAY_PUBLIC_KEY', ''),
|
||||
'private_key' => env('PAY_ALIPAY_PRIVATE_KEY', ''),
|
||||
'return_url' => '',
|
||||
'notify_url' => '',
|
||||
// mode 必须是【整数】:0=正式 1=沙箱 2=服务商(Yansongda\Pay\Pay::MODE_*)。
|
||||
// 写成 'normal'/'dev' 字符串会在拼网关 URL 时数组越界。
|
||||
'mode' => (int) env('PAY_ALIPAY_MODE', 0),
|
||||
'app_id' => env('PAY_ALIPAY_APP_ID', ''),
|
||||
// 应用私钥:PEM 全文、单行 base64(已在上方自动补全 PEM 头并识别 PKCS8/PKCS1),或私钥文件路径
|
||||
'app_secret_cert' => $alipaySecretCert,
|
||||
'app_public_cert_path' => $resolveCertPath((string) env('PAY_ALIPAY_APP_PUBLIC_CERT_PATH', '')),
|
||||
'alipay_public_cert_path' => $resolveCertPath((string) env('PAY_ALIPAY_PUBLIC_CERT_PATH', '')),
|
||||
'alipay_root_cert_path' => $resolveCertPath((string) env('PAY_ALIPAY_ROOT_CERT_PATH', '')),
|
||||
'return_url' => '',
|
||||
'notify_url' => '',
|
||||
],
|
||||
|
||||
// 微信支付(如需,在 buy() 中按相同方式调用 Pay::wechat(...) 即可)
|
||||
// 微信支付(v3 API)。注:微信早已下线官方沙箱,mode 留 0 即可;
|
||||
// 没有商户号时只能走 mock 或小额真实支付再退款。
|
||||
// 说明:真实的下单 / 验签 / 回调已迁到支付中心插件(addon/paycenter),
|
||||
// 这里保留仅为「既有部署的密钥来源」——插件 PayConfig 会用下面的值覆盖自己的同名项。
|
||||
'wechat' => [
|
||||
'mch_id' => env('PAY_WECHAT_MCH_ID', ''),
|
||||
'app_id' => env('PAY_WECHAT_APP_ID', ''),
|
||||
'secret_key' => env('PAY_WECHAT_SECRET_KEY', ''),
|
||||
'cert_client' => '',
|
||||
'cert_key' => '',
|
||||
'return_url' => '',
|
||||
'notify_url' => '',
|
||||
'mode' => (int) env('PAY_WECHAT_MODE', 0),
|
||||
'mch_id' => env('PAY_WECHAT_MCH_ID', ''),
|
||||
// SDK 按场景取键:mp → mp_app_id、mini → mini_app_id、app → app_id
|
||||
'app_id' => env('PAY_WECHAT_APP_ID', ''),
|
||||
'mp_app_id' => env('PAY_WECHAT_MP_APP_ID', env('PAY_WECHAT_APP_ID', '')),
|
||||
'mini_app_id' => env('PAY_WECHAT_MINI_APP_ID', env('PAY_WECHAT_APP_ID', '')),
|
||||
// APIv3 密钥
|
||||
'mch_secret_key' => env('PAY_WECHAT_SECRET_KEY', ''),
|
||||
// 商户 API 私钥:文件路径或 PEM 内容(插件会再做一次 PKCS8/PKCS1 判定)
|
||||
'mch_secret_cert' => env('PAY_WECHAT_SECRET_CERT', ''),
|
||||
// 商户 API 证书:用于提取证书序列号
|
||||
'mch_public_cert_path' => env('PAY_WECHAT_PUBLIC_CERT_PATH', ''),
|
||||
// 微信平台证书(回调验签用):留空时 SDK 凭 APIv3 密钥自动下载;
|
||||
// 生产环境建议落到本地并配置 [证书序列号 => 文件路径],避免每次请求都去拉取
|
||||
'wechat_public_cert_path' => [],
|
||||
'return_url' => '',
|
||||
'notify_url' => '',
|
||||
],
|
||||
];
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | YwxApp [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2026-2036 http://ywxapp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: ywxapp<admin@ywxapp.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
// API 限流配置(ywxapp\middleware\RateLimit 消费,注册于 app/api/middleware.php)。
|
||||
//
|
||||
// rules:正则(匹配 pathinfo,不区分大小写)=> ['limit' => 次数, 'window' => 窗口秒数]
|
||||
// 按声明顺序匹配,命中即用;limit = 0 表示该规则不限流。
|
||||
// default:未命中任何规则的兜底限流。
|
||||
return [
|
||||
// 兜底:每 IP 每分钟 120 次(普通客户端足够,脚本刷量会被拦)
|
||||
'default' => [
|
||||
'limit' => (int) env('RATELIMIT_DEFAULT', 120),
|
||||
'window' => 60,
|
||||
],
|
||||
|
||||
'rules' => [
|
||||
// 登录 / 登出:防爆破
|
||||
'#(login|logout)#i' => ['limit' => 10, 'window' => 60],
|
||||
|
||||
// 注册 / 找回密码 / 重置密码:防批量注册与撞库
|
||||
'#(register|retrieve|forget|reset|findpwd)#i' => ['limit' => 5, 'window' => 300],
|
||||
|
||||
// 验证码 / 短信 / 邮件发送:防短信轰炸
|
||||
'#(captcha|sms|verif|sendmail|send_?code|email)#i' => ['limit' => 5, 'window' => 60],
|
||||
|
||||
// 支付 / 下单:防并发刷单
|
||||
'#(pay|order|checkout)#i' => ['limit' => 20, 'window' => 60],
|
||||
],
|
||||
];
|
||||
+6
-6
@@ -16,8 +16,8 @@
|
||||
return [
|
||||
// SessionID 前缀(Cookie 名称)
|
||||
'prefix' => 'PHPSESSID',
|
||||
// 驱动方式
|
||||
'type' => 'file',
|
||||
// 驱动方式(集群/多副本部署请在 .env 设 SESSION_TYPE=redis,否则会随机掉登录态)
|
||||
'type' => env('SESSION_TYPE', 'file'),
|
||||
// 是否自动启动
|
||||
'auto_start' => true,
|
||||
// Session 保存路径(留空则使用 runtime_path() . 'session/',backend 应用为 runtime/addon/backend/session/)
|
||||
@@ -31,10 +31,10 @@ return [
|
||||
'data_compress' => false,
|
||||
// Cookie 域名
|
||||
'domain' => '',
|
||||
// 仅 HTTPS 传输
|
||||
'secure' => false,
|
||||
// 仅 HTTP 可见(防 XSS)
|
||||
'httponly' => false,
|
||||
// 仅 HTTPS 传输(生产 HTTPS 站点在 .env 设 SESSION_SECURE=true)
|
||||
'secure' => env('SESSION_SECURE', false),
|
||||
// 仅 HTTP 可见(防 XSS 窃取 SessionID)
|
||||
'httponly' => true,
|
||||
// 使用 Cookie 传递 SessionID
|
||||
'use_cookies' => true,
|
||||
// 客户端缓存限制
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | YwxApp [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2026-2036 http://ywxapp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: ywxapp<admin@ywxapp.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
// 蜘蛛真伪识别配置(ywxapp\library\SpiderVerify 消费)
|
||||
return [
|
||||
// DNS 服务器(用于 PTR 反查与 A/AAAA 正查)
|
||||
// 留空则自动探测:/etc/resolv.conf 的第一个 nameserver,探测不到用 223.5.5.5
|
||||
// 内网环境无外网 DNS 时可填本地 DNS,如 '192.168.1.1'
|
||||
'dns_server' => env('SPIDER_DNS_SERVER', ''),
|
||||
|
||||
// 说明:真伪判定走「PTR 反查 → 官方域名后缀 → A/AAAA 正查」三步,
|
||||
// 结果按 IP 缓存 24 小时(SpiderVerify::TTL),单次 DNS 硬超时 0.5 秒,
|
||||
// 连续失败 3 次熔断降级 5 分钟,不会拖慢站点。
|
||||
];
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | YwxApp 站点级 layui 主题配置
|
||||
// +----------------------------------------------------------------------
|
||||
// | 说明:这是「整站 layui 皮肤」的唯一变量源,对所有页面生效
|
||||
// | (后台 / 会员中心 / 前台 / 各插件前台),由全局中间件 ThemeInject 注入。
|
||||
// |
|
||||
// | 变量名与 public/static/theme/layui-theme.css 中的 --ywx-* 一一对应:
|
||||
// | primary -> --ywx-primary 主色(按钮/表单聚焦/进度条/时间轴)
|
||||
// | accent -> --ywx-accent 强调色(导航/分页当前页/上传/logo)
|
||||
// | radius -> --ywx-radius 统一圆角
|
||||
// | font -> --ywx-font 正文字体
|
||||
// |
|
||||
// | 改这里即可全站换肤,无需改动任何视图文件、也无需清理缓存。
|
||||
// | 后台可在「模板/主题」界面写入本文件(见 SiteTheme::save())。
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
// 主题开关:关闭后不注入任何主题样式(用于排查样式冲突)
|
||||
'enable' => true,
|
||||
|
||||
// 站点主题变量(值为空表示使用 layui-theme.css 内的兜底默认值)
|
||||
'variables' => [
|
||||
'primary' => '#d99cdd',
|
||||
'accent' => '#16baaa',
|
||||
'radius' => '2px',
|
||||
'font' => '-apple-system, BlinkMacSystemFont, \'Microsoft YaHei\', \'PingFang SC\', sans-serif',
|
||||
],
|
||||
];
|
||||
+33
-6
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user