Xuan's blog - 开发技术
type: channel
link: https://blog.ybyq.wang/category/dev-tech/
description: 前后端开发技术、框架使用、编程实战经验分享
items: 宝塔面板部署 Springboot+Vue+Sqlite 前后端项目教程
items: EchoTrace 微信聊天年度报告生成教程
items: 基于Spring Boot + Vue.js的GPT AI聊天应用：从开发到部署的完整实践
items: 飞牛NAS 配置 FRP 内网穿透：实现远程桌面访问
items: 零成本建站：将 Windows 电脑变身为个人网站服务器
items: 使用frp搭建高性能内网穿透服务（结合XAMPP和宝塔面板）
items: 获取家庭光猫超级管理员权限的多种方法
items: PHP插件开发中的一个错误：JSON直接输出导致网站首页异常
items: 解决宝塔面板SSL报错 - AttributeError: module 'acme_v2' has no attribute 'acme_v2'
items: 解决Typecho报错 - Unsupported operand types: int - string

宝塔面板部署 Springboot+Vue+Sqlite 前后端项目教程
type: item
date: 2026-01-12T19:02:00+08:00
description: 前言本文记录一个宠物商店后台管理系统项目在宝塔面板上的部署流程，前端使用 Vue，后端为 Spring Boot。下文按“先部署后端，再部署前端”的顺序来说明。这篇教程是半年前部署一个零工平台前后端项目时写的，写了一半停了，最近又部署了一个顺便把它完善一下。项目概述前端：vue 项目后端：Spring Boot 3.3.5 + Java 17 + SQLite 数据库域名：https://petad.xuan.cyou后端端口：8083前端项目部署前的设置配置说明：API 接口路径与环境设置一般项目支持两种常见的接口路径配置方式，请根据后端接口的实际路径选择对应的方案。场景一：接口已统一包含 /api 前缀（本项目选择）若后端所有接口已统一添加 /api 前缀（例如：/api/product/list），则前端环境变量配置如下：# .env.development VITE_APP_BASE_API=http://localhost:8083 # .env.production VITE_APP_BASE_API=https://petad.xuan.cyou在 axios 实例中直接使用该变量作为 baseURL：// src/api/request.js 片段 const service = axios.create({ baseURL: import.meta.env.VITE_APP_BASE_API || 'http://localhost:8083', timeout: 10000 })各接口定义中需添加 /api 前缀：// 示例接口：获取商品列表 export function getProductList(params) { return request({ url: '/api/product/list', // 添加/api前缀 method: 'get', params }) }场景二：接口未统一添加 /api 前缀若后端接口未统一添加 /api 前缀，则可将前缀直接配置在环境变量中：# .env.development VITE_APP_BASE_API=http://localhost:8083/api # .env.production VITE_APP_BASE_API=https://petad.xuan.cyou/api在 axios 配置中直接使用完整基础路径：// src/api/request.js 片段 const service = axios.create({ baseURL: import.meta.env.VITE_APP_BASE_API || 'http://localhost:8083/api', timeout: 10000 })此时，接口定义中不再需要 /api 前缀：// 示例接口：获取商品列表 export function getProductList(params) { return request({ url: '/product/list', // 无需/api前缀 method: 'get', params }) }通用配置：开发环境代理无论选择上述哪种场景，在开发环境下均需在 vite.config.js 中配置代理，以便正确转发请求至后端服务：// vite.config.js 片段 server: { proxy: { '/api': { target: 'http://localhost:8083', // 后端服务地址 changeOrigin: true }, '/images': { target: 'http://localhost:8083', changeOrigin: true } } }补充说明：路由模式与刷新 404 问题如果项目使用 Vue Router 的 history 模式，部署后刷新非首页路由可能出现 404 错误。此时需在 Web 服务器（如 Nginx）中配置伪静态规则，将请求重定向至 index.html。以 Nginx 为例，可添加如下配置：location / { try_files $uri $uri/ /index.html; }云服务器准备1.选购我选用的是阿里云的云服务器，如果只是临时部署这个项目作为演示，选择最低配的就可以。镜像直接选择宝塔的应用镜像就行如果有别的需求，尽量选择Debian，Centos Stream，Ubuntu等如果选择的是上面三个，你需要手动配置宝塔面板并开放访问端口2.查看面板登录信息宝塔配置1. 登录并进入面板2. 创建一个静态网站我用的是 SQLite，这里无需新创建数据库。申请免费SSL证书，启用https3. 上传项目前端代码将前端打包后的 dist 目录上传到这里…

EchoTrace 微信聊天年度报告生成教程
type: item
date: 2026-01-03T16:55:00+08:00
description: 今天分享一个免费开源的微信年度报告总结工具——EchoTrace，以及详细的使用教程。什么是 EchoTrace？EchoTrace 是一个完全本地运行的微信聊天记录分析工具，具有以下特点：完全本地运行：所有数据都在你的电脑上处理，不上传任何数据到云端，确保隐私安全解密并离线查看：可以安全地浏览完整的微信聊天记录导出为 HTML：将聊天记录导出为 HTML 格式，方便与朋友分享生成个性化报告：从数万条消息中提取有趣的数据，生成独一无二的年度回顾报告和双人报告使用前准备在开始之前，请确保：你的微信已升级到 PC 微信 4.0.x 以上版本已下载两个工具：wx_key.exe：用于提取微信数据库密钥echotrace.exe：EchoTrace 主程序详细使用步骤步骤 1：下载工具首先需要下载两个工具：wx_key.exe 和 echotrace.exe。步骤 2：提取数据库密钥双击打开 wx_key.exe 工具。打开后，点击「开始提取秘钥」按钮。步骤 3：复制数据库密钥提取成功后，复制获取到的数据库密钥（请妥善保管，后续配置需要使用）。步骤 4：打开 EchoTrace双击打开 echotrace.exe 主程序。步骤 5：进入设置界面点击左下角的「设置」按钮，进入配置界面。步骤 6：填入数据库密钥在设置界面中，将步骤 3 中复制的数据库密钥粘贴到「数据库密钥」字段。步骤 7：选择数据库根目录填写数据库根目录，建议使用「手动选择」方式。重要提示：选择目录时，只需选择到 xwechat_files 目录即可，不需要选择更深层的子目录。步骤 8：填写微信 WXID填写你的微信 WXID（注意：这里填写的是 WXID，不是微信昵称）。步骤 9：测试并保存配置填写完上述信息后，点击「测试」按钮验证配置是否正确。如果测试成功，即可保存配置。注意：如果出现「无法连接数据库」的报错，这是正常现象，点击「前往数据库管理」继续下一步操作。步骤 10：批量解密数据库在数据库管理界面中，点击「批量解密」按钮，对微信数据库进行解密处理。步骤 11：查看聊天记录解密完成后，返回聊天记录界面，即可看到完整的聊天内容。步骤 12：生成年度报告现在你可以查看生成的年度报告和双人报告了！这些报告会从你的聊天记录中提取有趣的数据，生成个性化的分析报告。示例截图[collapse status="false" title="展开查看更多"][/collapse]功能说明年度报告：展示你全年的聊天数据统计，包括消息数量、活跃时间、常用词汇等双人报告：分析你与特定好友的聊天记录，生成专属的双人互动报告导出功能：可以将聊天记录导出为 HTML 格式，方便保存和分享注意事项确保微信版本为 4.0.x以上，否则可能无法正常提取密钥所有操作均在本地完成，不会上传任何数据到服务器请妥善保管数据库密钥，避免泄露首次使用需要解密数据库，可能需要一些时间，请耐心等待项目地址EchoTrace 是一个开源项目，你可以在 GitHub 上查看源代码和更多文档：https://github.com/ycccccccy/echotracehttps://github.com/ycccccccy/wx_key

基于Spring Boot + Vue.js的GPT AI聊天应用：从开发到部署的完整实践
type: item
date: 2025-09-23T21:57:00+08:00
description: 前言一个人一个下午做成的，为了应对周四的作业答辩。🚀 项目概述本文将详细介绍一个基于Spring Boot后端和Vue.js前端的GPT AI聊天应用的完整开发流程。该项目集成了OpenAI、阿里云通义千问、DeepSeek、豆包、Kimi等多个主流AI平台，支持文本对话和图像生成功能，并实现了用户认证、会话管理、管理员面板、用户统计等完整的业务功能。体验地址：https://gpt.xuan.cyou/技术栈后端: Spring Boot 3.5.5, Java 17, SQLite, JPA/Hibernate前端: Vue.js 3, Vite, Pinia, JavaScript ES6+AI集成: OpenAI GPT-4, 阿里云通义千问, DeepSeek, 豆包, Kimi部署: 宝塔面板, Nginx, SSL证书📁 项目架构GPT-AI-Project/ ├── gpt-ai-java/ # Spring Boot 后端 │ ├── src/main/java/ │ │ ├── controller/ # REST API控制器 (用户, AI, 管理员等) │ │ ├── service/ # 业务逻辑层 (多AI平台服务, 用户统计等) │ │ ├── entity/ # 数据实体 (用户, 对话历史, 统计数据等) │ │ ├── config/ # 配置类 │ │ └── adopter/ # AI平台适配器 (OpenAI, Ali, DeepSeek, Kimi等) │ ├── src/main/resources/ │ │ └── application.yaml # 应用配置 │ └── pom.xml # Maven依赖配置 └── frontend/ # Vue.js 前端 ├── src/ │ ├── components/ # Vue组件 │ ├── views/ # 页面视图 (聊天, 图像, 管理面板, 用户后台等) │ ├── api/ # API调用封装 │ └── store/ # 状态管理 (Pinia) ├── public/ # 静态资源 └── package.json # NPM依赖配置🔧 核心功能实现1. 后端核心架构1.1 多AI平台集成项目支持OpenAI和阿里云通义千问两个AI平台，通过适配器模式实现：// AI控制器统一入口 @RestController @RequestMapping("/api/ai") public class AiController { @GetMapping("/chat/text/stream") public SseEmitter streamChat( @RequestParam String platform, @RequestParam String model, @RequestParam String message ) { // 根据平台选择对应的AI服务 (示例) if ("openai".equals(platform)) { return openAiController.sendTextStream(model, message); } else if ("ali".equals(platform)) { return aliAiController.sendTextStream(model, message); } // ... 此处添加对 deepseek, doubao, kimi 等平台的路由逻辑 return null; // or throw an exception } }1.2 流式响应实现使用Server-Sent Events (SSE) 实现实时流式对话：@PostMapping("/chat/text/stream") public SseEmitter sendTextStream(String model, String message) { SseEmitter emitter = new SseEmitter(0L); CompletableFuture.runAsync(() -> { try { // 流式读取AI响应并实时推送 openAIService.sendText(model, message, true, delta -> { emitter.send(SseEmitter.event().name("delta").data(delta)); }); emitter.send(SseEmitter.event().name("done").data("[DONE]")); emitter.complete(); } catch (Exception e…

飞牛NAS 配置 FRP 内网穿透：实现远程桌面访问
type: item
date: 2025-09-05T20:03:00+08:00
description: 实现飞牛NAS远程访问的四种方式FN Connect：最省事，但速度与稳定性受厂商服务与线路影响。DDNS：仅在“有公网 IPv4 且能做路由器端口映射”时有效；若处于 NAT/CGNAT（无公网 IPv4），不可用。FRP/反向隧道：无公网 IPv4 也可用，但需要一台云服务器并放行端口。同类方案还包括 Cloudflare Tunnel、SSH 反向隧道。异地组网：如 WireGuard、ZeroTier、Tailscale。建立虚拟专网后设备间像在同一内网，适合多设备与长期使用。补充说明若有固定公网 IP，可直接“公网 IP + 端口映射”访问；DDNS 只是动态地址解析的便捷手段。安全建议：如需暴露管理面板，建议开启认证或限制来源 IP；使用 FRP 时建议启用 auth.token 并在客户端同步配置。如需基于域名的 HTTP/HTTPS 穿透，可使用 http/https 代理类型，并在服务端放行 80/443 端口。一、准备工作云服务器一台，具备公网 IP。放行安全组端口：7000/TCP（或你配置的 bindPort）7500/TCP（FRPS 面板 Dashboard）需要映射的所有 remotePort（例如 5666、5667 等）飞牛 NAS 可安装并运行 FRPC 客户端。二、下载与部署 FRPS（服务端）1、采用默认配置并开放安全组无需修改默认配置，确保安全组已按上文放行。2、查看 FRPS 面板与配置文件在云服务器上，点击 7500 端口的 HTTP 访问 FRPS 面板。打开 FRPS 的配置文件，路径为 /www/dk_project/dk_app/frps/frps_8ZSp/data/frps.toml三、下载与配置 FRPC（客户端）1、参数说明serverAddr：云服务器公网 IP。type：协议类型（示例使用 tcp）。localIP：本机地址（FRPC 运行在飞牛 NAS 上，NAS 本地服务通常为 127.0.0.1 或 NAS 的内网 IP）。localPort：本地端口（飞牛面板默认 5666）。remotePort：云服务器上暴露的端口（需在安全组放行，且避免与现有服务端口冲突）。2、示例配置（穿透飞牛面板与另一端口）proxies name = "fnos" type = "tcp" localIP = "127.0.0.1" localPort = 5666 remotePort = 5666 proxies name = "fnos1" type = "tcp" localIP = "127.0.0.1" localPort = 5667 remotePort = 5667如需穿透其他端口，按相同步骤新增条目，并在服务端安全组放行对应的 remotePort。3、宝塔面板如需使用域名访问，在宝塔面板配置反向代理即可。

零成本建站：将 Windows 电脑变身为个人网站服务器
type: item
date: 2025-08-08T14:49:21+08:00
description: 一、环境准备1.1 所需软件与资源XAMPP: 集成了 Apache、MariaDB、PHP 和 Perl 的跨平台 Web 开发环境Typecho: 轻量级开源博客程序，基于 PHP公网访问方案: 内网穿透工具（花生壳、frp）或固定 IP 方案文本编辑器: 用于修改配置文件（如 VS Code、Notepad++ 等）二、本地服务器配置2.1 XAMPP 安装与初始化从Apache Friends官网下载适合您操作系统的XAMPP安装包执行安装程序，建议安装路径避开系统目录（如C:\Program Files），本指南使用D:\xampp安装完成后，启动XAMPP控制面板依次启动Apache和MySQL服务如果 MySQL 无法启动，请参见文末“附录A：常见问题与排查”。在浏览器中访问http://localhost验证服务器运行状态2.2 数据库创建Typecho 需要一个 MySQL 数据库来存储内容和配置信息：访问http://localhost/phpmyadmin/进入数据库管理界面点击左侧导航栏中的"新建"选项在表单中输入数据库名称（如typecho_db）选择字符集为utf8mb4_general_ci（支持完整的Unicode字符，包括Emoji）点击"创建"按钮完成数据库初始化安全提示: 在生产环境中，应为数据库创建专用用户并限制权限，而非使用默认的root账户。可在phpMyAdmin的"用户账户"→"添加用户"中完成此操作。三、Typecho博客部署3.1 程序文件安装从Typecho官方网站下载最新版本的程序包解压下载的压缩文件将解压后的所有文件复制到 XAMPP 的网站目录中：D:\xampp\htdocs\typecho\3.2 执行安装向导在浏览器中访问http://localhost/typecho/启动安装程序在欢迎页面点击"我准备好了，开始下一步"在数据库配置页面填写以下信息：数据库适配器: MySQL数据库地址: localhost数据库用户名: root（XAMPP 默认）数据库密码: （默认为空）数据库名: typecho_db（您在上一步创建的数据库）表前缀: typecho_（可保持默认）点击"确认，开始安装"按钮在站点配置页面设置管理员账户信息：站点名称站点描述管理员用户名管理员密码管理员邮箱完成安装后，您可以通过以下地址访问：博客前台：http://localhost/typecho/管理后台：http://localhost/typecho/admin/至此，本地博客系统已完成基础配置，可以进行文章发布和管理。3.3 Apache 网站根目录配置关键步骤: 为了让访问您的域名或IP时直接显示博客内容（而非XAMPP默认欢迎页），需要修改Apache配置，将根目录指向Typecho文件夹：在XAMPP控制面板中停止Apache服务备份原始配置文件：D:\xampp\apache\conf\httpd.conf使用文本编辑器打开httpd.conf文件搜索DocumentRoot并找到以下配置：# 原配置 DocumentRoot "D:/xampp/htdocs" <Directory "D:/xampp/htdocs">修改为指向Typecho目录：# 新配置 DocumentRoot "D:/xampp/htdocs/typecho" <Directory "D:/xampp/htdocs/typecho">保存文件并在XAMPP控制面板中重新启动Apache服务3.4 Typecho 站点 URL 配置当您通过任何域名或 IP 访问博客时，需要确保 Typecho 知道自己的正确地址，否则样式和链接可能无法正常工作：通过http://localhost/admin/访问博客后台在左侧菜单中选择"设置"→"基本设置"找到"站点地址"选项根据您的访问方式，将其设置为对应的地址：本地访问：http://localhost公网域名访问：http://您的域名（如http://myblog.com或http://1ko0641539qv6.vicp.fun）注意: URL 末尾不要添加斜杠/，以避免路径解析问题。点击页面底部的"保存设置"按钮刷新博客首页，此时所有样式和功能应正常显示四、实现公网访问的多种方案本章介绍几种将您的本地服务器发布到公网的方法，您可以根据自身条件选择最适合的方案。如需了解各种内网穿透工具的详细对比，请参阅我们的专题文章《内网穿透工具全面对比：选择最适合您的方案》。4.1 方案一：花生壳内网穿透（适合动态IP）内网穿透允许外部网络通过特定域名访问您本地的 Web 服务：注册并登录花生壳账号，下载安装花生壳客户端在客户端或官网控制台中添加新的映射配置映射参…

使用frp搭建高性能内网穿透服务（结合XAMPP和宝塔面板）
type: item
date: 2025-08-08T13:20:05+08:00
description: 前言这篇教程是用XAMPP已经在本地搭建好了Typecho博客网站，除了用花生壳，也可以用更好的frp做内网穿透，此篇教程就是使用frp搭建高性能内网穿透服务。一、准备工作1.1 所需资源一台具有公网IP的服务器（已安装宝塔面板）域名（可选，用于通过域名访问服务）本地环境（运行Web服务的计算机）1.2 了解frp工作原理frp分为服务端（frps）和客户端（frpc）两部分：服务端（frps）：部署在公网服务器上客户端（frpc）：部署在本地需要穿透的设备上1.3 域名与DNS解析（指向同一IP）将要对外提供访问的域名（如：www.example.com）添加一条 A 记录，指向 frps 所在服务器的公网IP（即宝塔所在服务器IP）。同一台服务器可承载多个域名，均需指向同一公网IP。常用验证方式：在本地终端执行：nslookup www.example.com 或 ping www.example.com，确认解析到的IP与服务器公网IP一致。解析生效后再进行后续的Nginx配置与证书申请。二、服务器端配置2.1 安装与版本说明首选方式（推荐）：在宝塔软件商店中安装并开启「frp管理器 1.1」。该管理器可一键完成服务端安装、版本选择、配置与启动，适合快速部署与可视化管理。可选方式（手动）：从 GitHub 发布页下载对应平台的 frp 二进制包，解压并通过 systemd 方式托管运行（适合有Linux基础的用户）。重要版本提示：自 v0.51.0 起，frp 配置文件已由 INI 切换为 TOML 格式。请确保服务端与客户端均使用 TOML，并注意版本匹配。2.2 配置frps服务端进行服务配置，这些是默认的端口，需要在防火墙中打开进行WEB服务配置，设置好用户名和密码，此默认端口也要打开进行token设置最后，开启服务，并下载此客户端frp注意事项：kcpBindPort 不应与 bindPort 相同，建议分配为不同端口（如 bindPort=15443，kcpBindPort=15444）。vhostHTTPPort/vhostHTTPSPort 为 frps 内部的虚拟主机端口，通常通过 Nginx 80/443 反向代理至此端口对外提供服务。auth.token 为客户端鉴权必须一致的密钥，修改后需重启 frps 并同步更新客户端。2.4 配置防火墙确保服务器防火墙开放了必要的端口：# 根据frps.toml中的配置开放以下端口 firewall-cmd --permanent --add-port=15443/tcp # bindPort firewall-cmd --permanent --add-port=15444/udp # kcpBindPort firewall-cmd --permanent --add-port=18080/tcp # vhostHTTPPort firewall-cmd --permanent --add-port=18443/tcp # vhostHTTPSPort firewall-cmd --permanent --add-port=16337/tcp # tcpmuxHTTPConnectPort firewall-cmd --permanent --add-port=7001/tcp # webServer端口 firewall-cmd --permanent --add-port=80/tcp # HTTP标准端口 firewall-cmd --reload三、客户端配置3.1 下载frp客户端在您本地计算机上，下载与服务器端相同版本的frp：可以下载宝塔中给出的0.53.2版本Windows用户：从GitHub下载对应的64位 X86Windows版本，本教程为第一个下载后解压到一个方便的位置，如D:\frp3.2 配置frpc客户端（TOML格式）在frp文件夹中创建/编辑frpc.toml文件（注意：不是frpc.ini），内容如下：# frpc.toml # 基本配置 - 连接到服务器 serverAddr = "服务器公网IP" serverPort = 15443 # 必须与frps.toml中的bindPort一致 # 认证token，必须与服务器端一致 auth.token = "与服务器端相同的令牌" # 代理规则配置 proxies name = "web-site" # 名称可自定义 type = "http" # 协议类型 localIP = "127.0.0.1" # 本地服务IP localPort = 80 # 本地服务端口 customDomains = ["www.example.com"] # 您的域名（需已解析到与f…

获取家庭光猫超级管理员权限的多种方法
type: item
date: 2025-08-07T16:11:00+08:00
description: 背景我家的移动宽带有一个公网IP，我只要把本地创建好的博客网站的80端口映射上去，就可以全网访问了。但是我家的路由器和光猫的管理中都没有端口转发或映射设置，于是想到了用超级管理员角色登录光猫后台。前言运营商（如中国移动、电信、联通）提供的光猫或家庭网关，为了网络稳定和管理方便，通常会限制用户的权限。我们通过网页（如 192.168.1.1）使用设备背面提供的普通账户登录后，会发现缺少端口映射（虚拟服务器）、桥接模式修改等核心功能。这些功能实际上都存在，但被隐藏在“超级管理员”账户下。获取这个超级账户的权限是开启所有高级功能的前提。本文档旨在系统性地梳理和总结获取此类设备超级管理员密码的常用技术手段，其逻辑顺序由简至繁，由通用到特定。方法一：尝试通用默认凭据这是最直接也是第一步应该尝试的方法。不同地区、不同设备批次的超级管理员密码可能遵循一定的规律。基本原理：设备固件中内置了一组或多组高权限的默认用户名和密码。常见用户名：telecomadmin (中国电信)CMCCAdmin (中国移动)常见密码：admintelecomnE7jA%5maDm8H%MdA操作步骤：登出当前的普通用户账户。尝试使用上述用户名和密码组合进行登录。结果：如果成功，将直接进入功能完整的管理后台。如果失败，则说明设备使用了独特的或动态的密码，需要进入下一步。方法二：漏洞利用 - 配置文件下载部分老旧固件存在安全漏洞，允许用户在未授权的情况下直接下载设备的配置文件，密码信息通常以明文或简单加密的形式存储在其中。基本原理：通过访问一个特定的 URL，触发设备的配置文件备份机制，从而下载到包含敏感信息的 .conf 或 .xml 文件。常见 URL：http://192.168.1.1/backupsettings.conf http://192.168.1.1/dumpcfg.conf操作步骤：在浏览器地址栏直接输入上述 URL 并访问。如果浏览器提示下载文件（如 backupsettings.conf），则表示漏洞存在。使用文本编辑器（如记事本、VS Code）打开下载的文件。在文件中搜索关键词，如 Password、telecomadmin、CMCCAdmin。查找类似 <Password>xxxxxxxx</Password> 的字段，引号或标签内的内容即为密码。结果：若成功下载并找到密码，则破解完成。若返回 404 Not Found 或无响应，则说明该漏洞已被修复。方法三：漏洞利用 - 前端审查元素在某些设备中，即使前端页面没有显示，但后台接口可能已经将超级管理员的密码信息作为隐藏字段的 value 值输出到了 HTML 源码中。基本原理：利用现代浏览器的开发者工具（F12）审查网页的 DOM 结构，寻找被隐藏的、但含有密码信息的表单元素。操作步骤：使用普通账户登录设备管理后台。在登录后的页面，按 F12 键打开开发者工具。切换到 元素 (Elements) 标签页。按 Ctrl + F 调出搜索框，在代码中搜索关键词 password、telecomadmin 等。重点关注搜索到的 标签，检查其 value 属性是否含有明文密码。结果：此方法成功率相对较低，现代设备通常不会犯此类错误。如果未找到有效信息，则需转向更底层的攻击方式。方法四：终极方案 - ADB 后门利用这是针对许多基于 Linux 内核的嵌入式设备（包括光猫）的最高级、最有效的破解方法。基本原理：许多设备在固件中保留了一个用于工厂调试的后门——ADB（Android Debug Bridge）服务。该服务默认关闭，但可以通过发送一个特定的网络请求来远程开启它。一旦开启，我们就可以通过 ADB 工具连接到设备的 Shell，获得系统底层访问权限，从而直接读取或修改配置文件中的密码。操作步骤：获取会话 ID (SessionID)使用普通账户登录后台。打开开发者工具（F12），切换到 网络 (Network) 标签。刷新页面或点击任一菜单，在网络请求列表中找到对后台 CGI 文件（如 content.asp）的请求。在请求的 载荷 (Payload) 或 标头 (Headers) 中找到并精确复制当前的 sessionid 值。此 ID 是后续操作的凭证，具有时效性。发送开启 ADB 的指令切换到开发者工具的 控制台 (Console) 标签。执行一段精心构造的 JavaScript fetch 命令，向设备发送一个 POST 请求。此请求利用上一步获取的 sessionid 模拟合法操作，请求开启 ADB 功能。示例代码（注意： body 中的参数，尤其是 menu_id 和 action_id，对不同型号设备可能不同，需要情报或逆向分析来确定）：fetch("http://192…

PHP插件开发中的一个错误：JSON直接输出导致网站首页异常
type: item
date: 2025-07-22T19:26:00+08:00
description: 问题描述最近在使用步数统计插件（WeFootStep）时，发现网站首页完全变成了一段JSON数据，而不是正常的HTML页面。具体表现为首页显示如下内容：{"results":" \u770b\u770b\u4f60\u662f\u4e0d\u662f\u201c\u8d5e\u535a\u6587\u76f2\u201d dobe\u3001IDEA\u3001pycharm<\/mark>\u7b49\r\n<\/p><\/a><\/li>..."}这完全破坏了网站的正常浏览体验。原因分析经过几天的艰苦排查，发现问题出在WeFootStep插件的Widget.php文件中的getStepDataJson()函数：/** * 获取步数JSON数据，用于AJAX请求 */ public function getStepDataJson() { $history = $this->getStepHistory(); $stats = $this->getStepStats(); $data = [ 'history' => $history, 'stats' => $stats ]; header('Content-Type: application/json'); echo json_encode($data); exit; }这个函数存在的问题是：函数直接设置了响应头为application/json输出JSON编码后的数据调用exit终止了PHP的执行流程这种实现方式本来是为AJAX请求设计的，但如果在普通页面加载过程中被误调用，就会导致整个页面只输出JSON数据。解决方案解决方案很简单：修改getStepDataJson()函数，让它只在确认是AJAX请求时才直接输出JSON并退出：/** * 获取步数JSON数据，用于AJAX请求 */ public function getStepDataJson() { $history = $this->getStepHistory(); $stats = $this->getStepStats(); $data = [ 'history' => $history, 'stats' => $stats ]; // 只在AJAX请求时才直接输出JSON并退出 if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') { header('Content-Type: application/json'); echo json_encode($data); exit; } // 如果不是AJAX请求，返回数据而不直接输出 return $data; }这个改进添加了一个检查机制，通过判断$_SERVER['HTTP_X_REQUESTED_WITH']是否为xmlhttprequest来确定当前是否是一个AJAX请求。如果是，才执行原来的行为；如果不是，则只返回数据而不直接输出。技术要点AJAX请求的识别：通常AJAX请求会包含X-Requested-With: XMLHttpRequest头，这是检测AJAX请求的标准方法。避免直接输出：在MVC架构的应用中，控制器方法通常不应该直接输出内容，而是返回数据让框架处理。避免无条件退出：exit或die会立即终止PHP的执行，应谨慎使用，尤其是在可能被其他代码调用的函数中。教训与最佳实践在开发PHP插件或组件时，应遵循以下原则：关注点分离：数据处理与输出应该分开，不要在获取数据的函数中直接输出。条件性输出：如果必须在函数中输出，应该有明确的条件控制。防御性编程：总是假设你的函数可能在意外的情况下被调用，添加适当的检查。明确文档：清晰记录函数的行为和副作用，特别是那些会改变HTTP头或直接输出的函数。这个简单的修改解决了首页显示JSON数据的问题，也提醒我们在插件开发中要注意代码的健壮性和兼容性。

解决宝塔面板SSL报错 - AttributeError: module 'acme_v2' has no attribute 'acme_v2'
type: item
date: 2025-07-17T14:35:46+08:00
description: 问题背景在使用宝塔面板设置网站SSL证书时，有时会遇到报错情况，常见的原因包括Python环境依赖缺失、面板组件损坏或环境冲突等。本文将介绍两种解决方案，帮助您快速恢复宝塔面板的SSL功能。常见错误示例以下是一个典型的错误示例：出错了，面板运行时发生错误！ AttributeError: module 'acme_v2' has no attribute 'acme_v2' REQUEST_DATE: 2023-07-17 10:29:45 VERSION: Debian GNU/Linux 12 (bookworm) x86_64(Py3.7.16) - 9.6.0 REMOTE_ADDR: 178.239.124.112 REQUEST_URI: POST /site?action=GetSSL REQUEST_FORM: {'siteName': 'blog.ybyq.wang'} Traceback (most recent call last): File "/www/server/panel/BTPanel/__init__.py", line 2525, in publicObject res = run_obj.run(toObject, defs, get) File "/www/server/panel/BTPanel/__init__.py", line 2472, in run result = getattr(toObject, get.action)(get) File "/www/server/panel/class/panelSite.py", line 2735, in GetSSL acme = acme_v2.acme_v2() AttributeError: module 'acme_v2' has no attribute 'acme_v2'错误分析从上述错误日志可以看出：错误类型：AttributeError - Python属性错误具体原因：module 'acme_v2' has no attribute 'acme_v2' - 找不到acme_v2模块中的acme_v2方法或类错误位置：在处理SSL证书申请时（/site?action=GetSSL）出现错误错误堆栈：最终在panelSite.py文件的2735行调用acme_v2.acme_v2()时失败这个错误通常是由于Python模块安装不完整、版本不兼容或模块损坏导致的。特别是在Debian系统上运行Python 3.7.16版本时，可能会出现此类依赖问题。解决方案一：重新安装依赖包在执行此操作前，强烈建议先做好磁盘快照或系统备份，以防万一出现问题可以快速回滚。步骤：首先执行以下命令：btpip install -r /www/server/panel/requirements.txt && bt restart代码解释：btpip install -r /www/server/panel/requirements.txt：使用宝塔面板的pip工具安装requirements.txt文件中列出的所有Python依赖包。requirements.txt包含宝塔面板正常运行所需的所有Python库。&&：这是命令连接符，表示左边命令执行成功后才会执行右边的命令。bt restart：重启宝塔面板服务，使新安装的依赖生效。此命令会重新安装所有宝塔面板依赖，包括处理SSL证书所需的acme_v2模块，有很大机会解决上述AttributeError问题。解决方案二：备份并重新安装面板如果解决方案一无效，可以尝试以下更彻底的方法。同样，务必先做好系统快照。步骤：备份现有Python环境：mv /www/server/panel/pyenv/ /www/backup/pyenv_backup重新安装宝塔面板：if [ -f /usr/bin/curl ];then curl -sSO https://download.bt.cn/install/install_panel.sh;else wget -O install_panel.sh https://download.bt.cn/install/install_panel.sh;fi;bash install_panel.sh ed8484bec代码解释：第一条命令：mv /www/server/panel/pyenv/ /www/backup/pyenv_backup：将宝塔面板的Python虚拟环境（pyenv目录）移动到备份位置。这个操作是将可能有问题的Python环境隔离，以便重新安装一个干净的环境。第二条命令：if [ -f /usr/bin/curl ]：检查系统是否安…

解决Typecho报错 - Unsupported operand types: int - string
type: item
date: 2025-07-15T20:57:44+08:00
description: 一场突如其来的网站崩溃今天做完插件，网站访客量激增。直接把我的2h2g负载拉满了，还好网站能打开，看到了点报错内容：Fatal error: Uncaught TypeError: Unsupported operand types: int - string in /www/wwwroot/blog.ybyq.wang/usr/themes/handsome/functions.php:113 Stack trace: #0 /www/wwwroot/blog.ybyq.wang/usr/themes/handsome/sidebar.php(25): online_users() #1 /www/wwwroot/blog.ybyq.wang/var/Widget/Themes/Handsome/index.php(113): require_once('/www/wwwroot/bl...') #2 /www/wwwroot/blog.ybyq.wang/var/Widget/Archive.php(1589): require('/www/wwwroot/bl...') #3 /www/wwwroot/blog.ybyq.wang/var/Widget/Archive.php(449): Widget_Archive->render() #4 /www/wwwroot/blog.ybyq.wang/index.php(15): Widget_Archive->archive() #5 {main} thrown in /www/wwwroot/blog.ybyq.wang/usr/themes/handsome/functions.php on line 113错误解读：这个错误信息告诉我们：在handsome主题的functions.php文件第113行，代码试图用一个整数(int)减去一个字符串(string)，这在PHP中是不被允许的操作。错误定位：顺藤摸瓜找到源头分析错误日志提供的线索：关键信息错误文件：handsome主题的functions.php错误位置：第113行错误类型：TypeError，类型不匹配调用链路：sidebar.php(25) → online_users()堆栈信息清晰地展示了错误的传播路径：侧边栏调用online_users()函数时触发了错误。这个函数用于统计当前在线的访客人数。问题定位：在线人数统计功能在特定情况下产生了类型不匹配，导致整个页面渲染失败。深入分析：代码脆弱性查看online_users()函数的核心逻辑：function online_users() { $filename = 'online.txt'; // 存储访客信息的文件 $onlinetime = 30; // 30秒在线有效期 $nowtime = $_SERVER['REQUEST_TIME']; // 当前时间戳(整数) $online = file($filename); // 读取文件内容为数组 $nowonline = array(); // 处理每条访客记录 foreach ($online as $line) { $row = explode('|', $line); // 分割用户ID和时间戳 $sesstime = trim($row[1]); // 获取时间戳(字符串!) // 问题代码就在这里 if (($nowtime - $sesstime) <= $onlinetime) { $nowonline[$row[0]] = $sesstime; } } return count($nowonline); }核心问题出在$nowtime - $sesstime这一运算：$nowtime是一个整数时间戳$sesstime是从文件读取的字符串警告：在线运行环境中，online.txt文件可能出现各种异常情况：存在空行某行格式错误，缺少分隔符|时间戳部分包含非数字字符当这些情况发生时，trim($row[1])可能返回空字符串或非数值内容，导致PHP在执行整数 - 字符串运算时抛出TypeError，整个程序终止运行。解决之道：防御性编程防御性编程思想：面对外部数据的不确定性，我们不能寄希望于数据永远完美，而应让代码能够优雅地处理各种异常情况。针对这个问题，我们需要两个关键改进：存在性检查：在使用数组元素前，先确认它们是否存在类型安全转换：在执行数学运算前，将可能的字符串显式转换为整数修改后的代码：function online_users() { $filename = 'online.txt'; $onlinetime = 30; $nowtime = $_SERVER['REQUE…