尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

PHP开发实战:从环境配置、代码调试到安全部署的完整解决方案

PHP开发实战:从环境配置、代码调试到安全部署的完整解决方案 1. 项目概述PHP开发中的“日常”与“战斗”干了十多年Web开发PHP就像我工具箱里那把用得最趁手的螺丝刀天天见天天用。但越是熟悉就越清楚它哪里容易“滑丝”哪里需要“上点油”。今天聊的“PHP中的常见问题和解决方案”不是什么高深莫测的底层原理剖析而是我们这些一线码农每天敲代码时真真切切会撞上的那些墙以及怎么用最直接、最有效的方式把墙给拆了或者绕过去。无论是刚入门的新手还是已经能熟练使用框架的老鸟都会在不同阶段遇到这些问题环境配置一团乱麻、代码写着写着就出各种警告和错误、安全漏洞防不胜防、性能瓶颈莫名其妙出现还有和那些“好邻居”比如Nginx、Docker、队列服务打交道时的各种水土不服。看看最近大家搜的热词就知道了痛点非常集中从最基础的“win10配置php环境变量”、“php环境配置教程”到进阶的“docker 拉取php8.3和nginx后,如何运行php站点”再到让人头疼的“php warning: php startup: unable to load dynamic library ‘imagick‘”以及安全领域的“php sql注入”、“php 文件上传”、“php rce”。这些问题贯穿了开发、调试、部署、运维的全生命周期。这篇文章我就结合自己踩过的无数个坑把这些高频问题掰开揉碎了讲不仅告诉你“怎么办”更重点说清楚“为什么”让你下次遇到时能自己举一反三从根儿上解决问题。2. 开发环境搭建与配置的“从入门到放弃”环境配置是每个PHPer的“第一课”也是最容易让人从入门到放弃的一课。问题往往不是PHP本身有多难而是它依赖的Web服务器、扩展库以及操作系统之间那剪不断理还乱的关系。2.1 核心痛点解析路径、扩展与版本冲突环境问题的核心九成九出在三个地方系统路径、扩展加载和多版本共存。系统路径问题典型症状就是命令行输入php -v和浏览器访问页面得到的版本信息不一致或者干脆提示“php不是内部或外部命令”。这本质上是系统不知道去哪里找你的PHP可执行文件。在Windows下你需要手动把PHP的安装目录比如C:\php8.3添加到系统的PATH环境变量里。而在macOS或Linux上如果你用包管理器如Homebrew安装它通常会帮你处理好但如果你手动编译安装或者使用了像MAMP、XAMPP这样的集成环境就需要特别注意它们的PHP是否被设为默认。注意很多新手在mac上用了MAMP Pro但命令行还是系统自带的PHP。这就是因为MAMP的PHP路径通常是/Applications/MAMP/bin/php/php8.x.x/bin没有加入你的shell配置文件如~/.zshrc或~/.bash_profile。你需要手动添加一行export PATH/Applications/MAMP/bin/php/php8.x.x/bin:$PATH然后执行source ~/.zshrc生效。这就是“mac mamp pro 的php如何做全局替换电脑内部的php”这个搜索背后的真实需求。扩展加载失败这是最经典的“Warning”和“Fatal error”来源。错误信息通常长这样PHP Warning: PHP Startup: Unable to load dynamic library ‘imagick.so‘ (tried: /usr/lib/php/...)。这背后是一连串的检查链扩展文件是否存在php.ini里extensionimagick指定的.soLinux/mac或.dllWindows文件是否在extension_dir指定的目录里。依赖库是否满足像imagick这种扩展背后依赖ImageMagick系统库。如果系统没装或者版本不对PHP扩展照样加载失败。线程安全TS与非线程安全NTS匹配在Windows上尤其重要。你的PHP是TS版本却加载了NTS的扩展DLL一定会失败。通过php -i | grep “Thread Safety”可以查看。多版本共存与管理现代项目可能要求你在PHP 7.4和8.3之间切换。手动改PATH和php.ini太低效。这时候就需要版本管理工具。在Linux/mac上phpbrew是神器在Windows上可以用phpenv或者更通用的Docker来隔离环境。2.2 实操方案从零搭建一个可用的PHP开发环境我们以在macOS上搭建一个纯净的、可多版本切换的PHP环境为例这是最接近生产环境的本地开发方式。第一步使用Homebrew安装核心依赖和PHPHomebrew是macOS的包管理器能极大简化安装过程。# 1. 安装Homebrew如果尚未安装 /bin/bash -c “$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)” # 2. 添加Homebrew的PHP库 brew tap shivammathur/php # 3. 安装你需要的PHP版本例如8.3和7.4 brew install shivammathur/php/php8.3 brew install shivammathur/php/php7.4 # 4. 安装ComposerPHP的依赖管理工具 brew install composer安装后PHP 8.3的可执行文件路径通常是/usr/local/opt/php8.3/bin/php。但此时直接运行php命令可能还是系统自带的。第二步链接并切换PHP版本Homebrew安装的PHP不会自动链接到全局。你需要手动链接并配置shell环境。# 停止所有已链接的PHP brew unlink php7.4 2/dev/null brew unlink php8.3 2/dev/null # 链接你想要使用的版本例如8.3 brew link --overwrite --force php8.3 # 配置你的shell以zsh为例编辑 ~/.zshrc echo ‘export PATH“/usr/local/opt/php8.3/bin:$PATH”’ ~/.zshrc echo ‘export PATH“/usr/local/opt/php8.3/sbin:$PATH”’ ~/.zshrc source ~/.zshrc # 验证版本 php -v # 应该显示8.3.x现在你的命令行PHP已经切换到了8.3。要切换回7.4只需重复上述brew unlink和brew link步骤并更新.zshrc中的路径即可。这比修改系统默认PHP安全得多。第三步安装和配置必要的扩展PHP很多功能通过扩展实现。例如安装redis扩展# 使用pecl命令安装pecl随PHP一起安装 pecl install redis # 安装过程中可能会询问一些选项通常直接回车用默认值即可。 # 安装成功后pecl会提示你需要在php.ini中添加 extensionredis.so找到你的php.ini文件位置通过php --ini命令查看在文件末尾加上extensionredis.so。保存后重启你的Web服务器或PHP-FPM服务使用php -m | grep redis检查扩展是否加载成功。实操心得关于php.ini我强烈建议不要直接修改主配置文件。而是在php.ini所在的conf.d目录如果没有就创建一个里为每个扩展创建单独的.ini文件比如redis.ini里面只写extensionredis.so。这样管理起来清晰禁用某个扩展时直接删除或重命名文件即可避免在主配置文件中误操作。3. 代码层面的典型错误与高效调试环境配好了开始写代码另一类问题就来了语法错误、运行时警告、逻辑Bug。这些问题如果不善用工具和方法调试起来会非常耗时。3.1 语法与运行时错误从Warning到FatalPHP的错误级别从提醒到致命分很多种。理解它们有助于快速定位问题。E_NOTICE / E_WARNING非致命错误脚本会继续执行。比如使用未定义的变量E_NOTICE或include一个不存在的文件E_WARNING。在开发环境你应该设置error_reporting(E_ALL)来显示所有错误帮助提前发现隐患。但在生产环境必须用error_reporting(0)或设置display_errors Off来避免泄露敏感信息。E_ERROR / E_PARSE致命错误。语法错误E_PARSE会在脚本运行前就被解析器发现。调用一个不存在的函数或类方法会引发E_ERROR脚本会立即终止。常见场景与解决“Undefined variable $xxx”养成好习惯变量使用前先初始化。或者使用isset()或??空合并运算符进行判断$name $_GET[‘name’] ?? ‘default’;。“Cannot modify header information – headers already sent”这是最经典的错误之一。原因是在调用header()、setcookie()等函数向浏览器发送HTTP头之前已经有内容哪怕是空格或UTF-8 BOM被输出。解决方案检查php.ini中是否开启了output_buffering它可以缓冲输出。确保在?php标签之前和?标签之后没有空格或空行。如果文件是UTF-8编码确保保存为“无BOM”的UTF-8格式。将所有的header()操作放在业务逻辑的最前端在输出任何HTML内容之前完成。3.2 使用Xdebug进行深度调试打印var_dump和die是初级调试法效率低。专业开发离不开调试器。Xdebug是PHP调试的事实标准。安装Xdebug以macOS Homebrew的PHP为例# 使用pecl安装 pecl install xdebug安装后在php.ini或conf.d目录下的配置文件中进行配置。配置是关键一个适用于IDE如VSCode、PHPStorm调试的配置如下[xdebug] zend_extension“xdebug.so” # Windows上是 zend_extension“php_xdebug.dll” xdebug.modedevelop,debug # Xdebug 3.x版本的关键配置定义其模式 xdebug.start_with_requestyes # 对每个请求都启动调试也可设为trigger通过GET/POST参数触发 xdebug.client_port9003 # 调试客户端监听端口默认9003老版本是9000 xdebug.client_host“127.0.0.1” # 调试客户端你的IDE的IP地址 xdebug.idekey“VSCODE” # IDE标识与IDE配置对应 xdebug.log“/tmp/xdebug.log” # 可选启用日志便于排查连接问题在VSCode中配置安装PHP Debug扩展。在项目根目录创建或编辑.vscode/launch.json添加配置{ “version”: “0.2.0”, “configurations”: [ { “name”: “Listen for Xdebug”, “type”: “php”, “request”: “launch”, “port”: 9003, “pathMappings”: { “/absolute/path/on/your/server”: “${workspaceFolder}” } } ] }这里的pathMappings至关重要它把服务器上的文件路径映射到你本地项目的路径。如果你用Docker或虚拟机服务器路径可能是/var/www/html需要正确映射。开始调试在VSCode中打开你的PHP项目在代码行号左侧点击设置断点红点。按F5或点击“运行和调试”侧边栏的绿色三角启动调试监听状态栏变橙。用浏览器访问你的本地项目如http://localhost:8080。当执行到断点处时浏览器请求会挂起VSCode会激活显示当前所有变量、调用堆栈你可以逐行执行F10、步入函数F11彻底洞察代码执行过程。实操心得Xdebug连接失败是常事。首先检查php.ini配置是否正确特别是xdebug.mode和xdebug.client_port。其次确保防火墙开放了9003端口。最有用的是开启xdebug.log查看日志文件里面会详细记录Xdebug尝试连接你IDE的每一步是排查问题的黄金依据。3.3 错误处理与日志记录的最佳实践除了调试完善的错误处理和日志记录是线上项目稳定的基石。自定义错误处理器使用set_error_handler()和set_exception_handler()可以捕获非致命错误和未捕获的异常进行统一处理比如记录到日志、发送告警而不是直接显示给用户。set_error_handler(function($errno, $errstr, $errfile, $errline) { // 将错误信息格式化为字符串写入日志 $message sprintf(“[%s] Error %s: %s in %s on line %d”, date(‘Y-m-d H:i:s’), $errno, $errstr, $errfile, $errline); error_log($message, 3, ‘/path/to/your/error.log’); // 3表示写入文件 // 生产环境可以返回一个友好的错误页面 if (!in_array($errno, [E_NOTICE, E_WARNING])) { // 非Notice/Warning错误 http_response_code(500); echo ‘系统内部错误请联系管理员。’; exit; } // 返回false将允许PHP内建错误处理器继续执行 return false; }); // 异常处理器 set_exception_handler(function($exception) { error_log(“Uncaught Exception: “ . $exception-getMessage(), 3, ‘/path/to/your/exception.log’); http_response_code(500); echo ‘系统内部错误请联系管理员。’; exit; });使用Monolog进行高级日志记录对于复杂项目推荐使用monolog/monolog这个强大的日志库。它可以轻松地将日志写入文件、数据库、Syslog、Slack、Email等。require ‘vendor/autoload.php’; // Composer自动加载 use Monolog\Logger; use Monolog\Handler\StreamHandler; // 创建一个日志频道 $log new Logger(‘my_app’); $log-pushHandler(new StreamHandler(‘path/to/your/app.log’, Logger::WARNING)); // 添加记录 $log-warning(‘这是一个警告’, [‘user’ ‘john’, ‘ip’ ‘192.168.1.1’]); $log-error(‘这是一个错误’, [‘exception’ $e]); // $e是一个异常对象通过不同的Handler和Formatter你可以实现按级别分文件存储、日志轮转、结构化日志JSON格式等高级功能极大方便了后续的日志分析和监控。4. 安全漏洞防御的艺术与实战PHP的灵活有时也意味着安全上的“坑”比较多。安全无小事以下几个是必须严防死守的阵地。4.1 SQL注入头号威胁与根治方案SQL注入的原理是攻击者通过构造特殊的输入改变原有SQL语句的语义从而执行非预期的数据库操作。比如登录场景$sql “SELECT * FROM users WHERE username ‘{$_POST[‘username’]}’ AND password ‘{$_POST[‘password’]}’”;如果用户输入admin’ --那么--后面的密码验证就被注释掉了直接以管理员身份登录。解决方案绝对不要再用字符串拼接必须使用参数化查询Prepared Statements。使用PDO推荐支持多种数据库$pdo new PDO(‘mysql:hostlocalhost;dbnametest;charsetutf8mb4’, ‘username’, ‘password’); $stmt $pdo-prepare(“SELECT * FROM users WHERE username :username AND password :password”); $stmt-execute([ ‘:username’ $_POST[‘username’], ‘:password’ hash(‘sha256’, $_POST[‘password’]) // 密码应加盐哈希存储此处仅为示例 ]); $user $stmt-fetch(PDO::FETCH_ASSOC);使用MySQLi$mysqli new mysqli(‘localhost’, ‘username’, ‘password’, ‘test’); $stmt $mysqli-prepare(“SELECT * FROM users WHERE username ? AND password ?”); $stmt-bind_param(‘ss’, $username, $password); // ‘ss’表示两个字符串参数 $username $_POST[‘username’]; $password hash(‘sha256’, $_POST[‘password’]); $stmt-execute(); $result $stmt-get_result(); $user $result-fetch_assoc();参数化查询将用户输入的数据纯粹地作为参数传递给数据库引擎与SQL语句的结构分离从根本上杜绝了注入的可能。这是唯一被广泛认可的根治方法转义函数如mysqli_real_escape_string在复杂场景下仍可能被绕过。4.2 文件上传与目录遍历漏洞文件上传功能如果处理不当攻击者可能上传Webshell如“免杀php大马”从而控制服务器。防御策略白名单验证文件类型不要依赖客户端验证或文件的MIME类型$_FILES[‘file’][‘type’]这很容易伪造。应使用服务器端检查文件扩展名并结合finfo_file()函数检查文件的真实类型。$allowedExtensions [‘jpg’, ‘png’, ‘gif’, ‘pdf’]; $fileExtension strtolower(pathinfo($_FILES[‘file’][‘name’], PATHINFO_EXTENSION)); if (!in_array($fileExtension, $allowedExtensions)) { die(‘文件类型不允许。’); } $finfo finfo_open(FILEINFO_MIME_TYPE); $mime finfo_file($finfo, $_FILES[‘file’][‘tmp_name’]); finfo_close($finfo); $allowedMimes [‘image/jpeg’, ‘image/png’, ‘image/gif’, ‘application/pdf’]; if (!in_array($mime, $allowedMimes)) { die(‘文件MIME类型不合法。’); }重命名上传的文件不要使用用户上传的文件名。生成一个随机的文件名如UUID并保留正确的扩展名。$newFileName bin2hex(random_bytes(16)) . ‘.’ . $fileExtension; $uploadPath ‘/var/www/uploads/’ . $newFileName;设置存储目录权限上传目录不应有执行权限。在Linux上设置目录权限为755文件权限为644。更好的做法是将上传目录放在Web根目录之外然后通过PHP脚本来读取和输出文件。防止目录遍历在处理文件路径时要防止用户输入../../../etc/passwd这样的路径。使用basename()函数获取文件名或使用realpath()函数解析绝对路径并检查解析后的路径是否在你允许的基目录下。$baseDir ‘/var/www/safe_dir/’; $userPath $_GET[‘file’]; // 假设通过参数传递文件名 $realPath realpath($baseDir . $userPath); if ($realPath false || strpos($realPath, $baseDir) ! 0) { // 路径解析失败或不在基目录内 die(‘非法文件访问。’); }4.3 命令执行RCE与代码注入eval()、system()、exec()、shell_exec()、passthru()这些函数非常危险如果其参数完全或部分由用户可控就可能造成远程命令执行RCE。防御策略绝对禁止用户输入直接进入这些函数。这是铁律。如果业务必须执行系统命令如调用外部工具请使用白名单严格限制可执行的命令和参数。对参数进行严格的过滤和转义。在PHP中可以使用escapeshellarg()或escapeshellcmd()函数但它们并非万能需谨慎使用。尽可能使用更安全的替代方案比如PHP内置的函数或库来完成功能。禁用危险函数在生产环境的php.ini中通过disable_functions指令禁用不必要的危险函数。disable_functions eval,exec,passthru,shell_exec,system,proc_open,popen,parse_ini_file,show_source,phpinfo小心反序列化“php序列化中文”可能涉及序列化操作。unserialize()函数如果反序列化用户可控的数据可能导致对象注入攻击触发类的__wakeup()或__destruct()方法中的恶意代码。解决方案不要反序列化不可信的数据。如果必须可以考虑使用JSON等更安全的格式进行数据交换或者使用PHP 7引入的allowed_classes参数限制可反序列化的类。4.4 会话安全与跨站脚本XSS会话安全会话固定攻击者获取或设置一个已知的会话ID诱导用户使用此ID登录从而劫持用户会话。防御用户登录成功后务必使用session_regenerate_id(true)重新生成会话ID。会话劫持通过XSS等手段窃取用户的会话Cookie。防御设置Cookie的HttpOnly属性session.cookie_httponly 1in php.ini防止JavaScript访问对于重要操作使用Secure属性仅HTTPS传输并考虑绑定用户IP或浏览器指纹。XSS防御核心原则是对输出进行转义。输出到HTML上下文使用htmlspecialchars()函数将,”,’,,等字符转换为HTML实体。echo ‘你好, ‘ . htmlspecialchars($userInput, ENT_QUOTES, ‘UTF-8’);输出到JavaScript或HTML属性需要更谨慎。现代框架如Laravel的Blade、Symfony的Twig都提供了自动转义机制。如果纯PHP开发务必根据输出上下文选择合适的转义函数。设置Content Security Policy (CSP)这是一个强大的深层防御策略。通过HTTP头Content-Security-Policy你可以告诉浏览器只允许加载指定来源的脚本、样式、图片等可以有效缓解甚至完全阻止XSS攻击。5. 性能优化与生产环境部署代码写安全了接下来就要考虑性能和稳定部署。性能问题常常在量变引起质变时才被发现提前优化事半功倍。5.1 OpCache必开的性能加速器PHP是解释型语言每次执行脚本都需要经历“词法分析 - 语法分析 - 编译为Opcode - 执行”的过程。OPcache通过将编译后的Opcode缓存在内存中跳过了耗时的编译阶段直接执行能极大提升PHP性能尤其是在框架应用中。启用与配置php.ini[opcache] opcache.enable1 ; 启用OpCache opcache.memory_consumption128 ; 分配多少MB内存给OpCache根据项目大小调整128-256是常见值 opcache.interned_strings_buffer8 ; 存储驻留字符串的内存大小有助于节省内存 opcache.max_accelerated_files10000 ; 缓存的文件数量上限设置足够大以覆盖所有文件 opcache.revalidate_freq2 ; 检查脚本是否更新的时间间隔秒生产环境可以设置大一些如60 opcache.fast_shutdown1 ; 启用快速关闭提升清理速度 opcache.enable_cli0 ; 命令行环境一般不需要除非运行CLI脚本也需加速生产环境部署后务必启用OpCache。你可以通过phpinfo()页面或php -i | grep opcache来确认它是否已启用。5.2 数据库查询优化数据库往往是性能瓶颈所在。索引是王道为WHERE、JOIN、ORDER BY子句中的列添加合适的索引。使用EXPLAIN命令分析你的查询语句查看是否用上了索引。避免N1查询问题这是ORM如Eloquent、Doctrine中常见的问题。循环中多次查询关联数据。应使用“预加载”Eager Loading一次性取出所有关联数据。// 糟糕的N1查询 $posts Post::all(); foreach ($posts as $post) { echo $post-author-name; // 每次循环都执行一次查询获取作者 } // 优化使用with预加载 $posts Post::with(‘author’)-get(); foreach ($posts as $post) { echo $post-author-name; // 作者信息已一次性取出 }缓存查询结果对于不经常变化但频繁读取的数据如配置、热门文章列表使用缓存如Redis、Memcached来存储查询结果。$cacheKey ‘hot_articles’; if (!$articles $redis-get($cacheKey)) { $articles $db-query(“SELECT * FROM articles ORDER BY views DESC LIMIT 10”); $redis-setex($cacheKey, 3600, serialize($articles)); // 缓存1小时 } else { $articles unserialize($articles); }5.3 使用队列处理耗时任务用户注册后发送欢迎邮件、处理上传的视频、生成报表这些任务如果放在Web请求中同步执行会严重拖慢响应速度甚至因超时导致请求失败。消息队列如Redis、RabbitMQ、Beanstalkd是解决此问题的标准方案。以使用Redis实现一个简单的异步邮件发送为例生产者Web应用将任务数据推入Redis列表。$redis new Redis(); $redis-connect(‘127.0.0.1’, 6379); $jobData json_encode([‘to’ ‘userexample.com’, ‘subject’ ‘Welcome’, ‘body’ ‘…’]); $redis-lPush(‘email_queue’, $jobData); // 将任务推入队列消费者独立的CLI进程常驻后台从队列中取出任务并执行。// consumer.php $redis new Redis(); $redis-connect(‘127.0.0.1’, 6379); while (true) { // BRPOP 是阻塞式弹出队列为空时会等待 $job $redis-brPop(‘email_queue’, 0); // 0表示无限等待 $data json_decode($job[1], true); // 调用发送邮件的函数 sendEmail($data[‘to’], $data[‘subject’], $data[‘body’]); echo “Email sent to ” . $data[‘to’] . PHP_EOL; }然后使用Supervisor等进程管理工具来守护这个消费者脚本确保它意外退出后能自动重启。关于“php rabbmitmq的路由模式和普通模式”这指的是RabbitMQ这种更专业的消息队列中间件的不同消息分发模式。简单说普通模式简单队列/工作队列一个生产者一个队列多个消费者竞争消费每条消息只被一个消费者处理。适合任务分发。路由模式Direct Exchange生产者将消息发送到交换机Exchange并指定一个路由键Routing Key。队列绑定到交换机时也指定一个路由键。只有路由键匹配的消息才会被路由到该队列。适合根据消息特征进行选择性消费的场景。5.4 Docker化部署与Nginx配置Docker提供了环境一致性是现代化部署的首选。“docker 拉取php8.3和nginx后,如何运行php站点”和“docker 部署时,不能正常运行php文件,运行php文件就直接下载文件”是典型问题。一个简单的docker-compose.yml示例version: ‘3.8’ services: nginx: image: nginx:alpine ports: - “8080:80” volumes: - ./code:/var/www/html # 挂载你的PHP代码 - ./nginx.conf:/etc/nginx/conf.d/default.conf # 挂载自定义Nginx配置 depends_on: - php php: image: php:8.3-fpm-alpine volumes: - ./code:/var/www/html # 可以在这里构建自定义镜像安装扩展或使用 volumes 挂载 php.ini关键的Nginx配置nginx.confserver { listen 80; server_name localhost; root /var/www/html/public; # Laravel等框架的入口在public目录 location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { # 解决“直接下载PHP文件”问题的核心配置 fastcgi_pass php:9000; # 这里指向php-fpm服务名和端口 fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }“运行php文件就直接下载文件”这个问题99%的原因是Nginx没有正确将PHP文件传递给PHP-FPM处理。检查点location ~ \.php$配置块是否存在且正确。fastcgi_pass指令的地址和端口是否正确Docker中应为服务名php和内部端口9000。fastcgi_param SCRIPT_FILENAME是否正确指向了文件在容器内的真实路径$document_root$fastcgi_script_name。Nginx配置修改后是否重载了nginx -s reload。“nginx php项目如何禁止直接url访问runtime”这通常是针对ThinkPHP等框架其运行时目录runtime不应被Web直接访问。在Nginx配置中为该目录添加一个拒绝所有访问的规则即可location ^~ /runtime/ { deny all; return 403; }^~前缀表示如果匹配此规则则不再检查其他正则location直接拒绝访问。
返回列表