// +---------------------------------------------------------------------- declare (strict_types = 1); namespace addon\blog\controller\backend; use think\facade\Request; use think\facade\View; use think\facade\Config; use think\facade\Cache; use ywxapp\controller\BackendBase; /** * Setting 类 * * @author ywxapp */ class Setting extends BackendBase { /** * Summary of needLogin * @var array */ /** * Summary of needRight * @var array */ protected $noNeedVerify = []; /** * 控制器初始化 _initialize * @return void */ protected function initialize() {} /** * 系统设置首页 */ public function index() { // 配置改存统一插件配置表(不再读写 PHP 配置文件,避免任意代码写入) $settings = [ 'site' => $this->getConfig('site'), 'seo' => $this->getConfig('seo'), 'email' => $this->getConfig('email'), 'upload' => Config::get('upload') ]; return View::fetch('setting/index', ['settings' => $settings]); } /** * 基本设置 */ public function site() { if (Request::isPost()) { $data = Request::post(); $this->saveConfig('site', $data); return json(['code' => 1, 'msg' => '基本设置已保存']); } $settings = $this->getConfig('site'); return View::fetch('setting/site', ['settings' => $settings]); } /** * SEO设置 */ public function seo() { if (Request::isPost()) { $data = Request::post(); $this->saveConfig('seo', $data); return json(['code' => 1, 'msg' => 'SEO设置已保存']); } $settings = $this->getConfig('seo'); return View::fetch('setting/seo', ['settings' => $settings]); } /** * 邮件设置 */ public function email() { if (Request::isPost()) { $data = Request::post(); $this->saveConfig('email', $data); return json(['code' => 1, 'msg' => '邮件设置已保存']); } $settings = $this->getConfig('email'); return View::fetch('setting/email', ['settings' => $settings]); } /** * 保存配置 */ /** * 保存配置到统一插件配置表 * * 之前的实现把 Request::post() 整个 var_export 写进 config/*.php, * 等于允许任意 PHP 代码写入(RCE),且 app_path() 在插件上下文指向不存在的目录 * (写入静默失败)。改为走 AddonService::config() 存库。 */ private function saveConfig($key, $data) { // 分组白名单 if (!in_array($key, ['site', 'seo', 'email'], true)) { return; } // 只接受标量值,键名做安全校验(防注入与数组嵌套) $clean = []; foreach ((array) $data as $k => $v) { $k = (string) $k; if (!preg_match('/^[a-zA-Z0-9_]+$/', $k)) { continue; } if (is_scalar($v)) { $clean[$k] = (string) $v; } elseif (is_array($v)) { $clean[$k] = json_encode($v, JSON_UNESCAPED_UNICODE); } } if (empty($clean)) { return; } // 以 "<分组>.<字段>" 形式存到插件配置表 $prefixed = []; foreach ($clean as $k => $v) { $prefixed[$key . '.' . $k] = $v; } \ywxapp\service\AddonService::config('blog', $prefixed); \ywxapp\service\AddonService::clearConfigCache('blog'); } /** * 读取某分组的配置(从插件配置表还原为不带前缀的数组) */ private function getConfig($key) { $all = \ywxapp\service\AddonService::config('blog'); $prefix = $key . '.'; $out = []; foreach ((array) $all as $k => $v) { if (strpos((string) $k, $prefix) === 0) { $out[substr((string) $k, strlen($prefix))] = $v; } } return $out; } }