首页
直播
壁纸
友链
搜索
1
微信小程序支付全链路实战:JSAPI 下单、调起支付、回调验签与退款
266 阅读
2
微信小程序云开发实战:云函数、云数据库与云存储的正确使用姿势
256 阅读
3
微信小程序自定义 tabBar 实战:custom-tab-bar 从适配到深色模式
255 阅读
4
微信小程序 Skyline 渲染引擎实战:worklet 动画从原理到落地
253 阅读
5
微信小程序分包进阶:独立分包、预下载与分包异步化实战
246 阅读
服务器运维
后端技术
前端技术
梯子
数据库
小程序
登录
搜索
标签搜索
fastadmin
Redis
微信小程序
前端开发
RabbitMQ
Go
服务器
codex
buildadmin
小程序
mysql
Nginx
Docker
Vue3
Node.js
MySQL优化
Linux
TypeScript
JWT
PHP
沿途的风景
累计撰写
74
篇文章
累计收到
0
条评论
首页
栏目
服务器运维
后端技术
前端技术
梯子
数据库
小程序
页面
直播
壁纸
友链
搜索到
74
篇与
» admin
的结果
2026-03-02
Node.js JWT 认证完整实现:注册登录到权限控制
前言JWT(JSON Web Token)是现代 Web 应用中最流行的认证方案之一。本文将使用 Node.js + Express 实现一个完整的 JWT 认证系统,包含注册、登录、Token 刷新和权限控制。一、项目初始化mkdir jwt-auth && cd jwt-auth npm init -y npm install express jsonwebtoken bcryptjs cors dotenv npm install -D typescript @types/express @types/node ts-node-dev二、项目结构src/ ├── config/ │ └── index.ts ├── middleware/ │ └── auth.ts ├── routes/ │ └── auth.ts ├── utils/ │ └── jwt.ts ├── types/ │ └── index.ts └── app.ts三、核心实现3.1 JWT 工具类// src/utils/jwt.ts import jwt from "jsonwebtoken"; import { config } from "../config"; export interface JwtPayload { userId: string; role: string; } export function signAccessToken(payload: JwtPayload): string { return jwt.sign(payload, config.JWT_SECRET, { expiresIn: "15m", }); } export function signRefreshToken(payload: JwtPayload): string { return jwt.sign(payload, config.JWT_REFRESH_SECRET, { expiresIn: "7d", }); } export function verifyAccessToken(token: string): JwtPayload { return jwt.verify(token, config.JWT_SECRET) as JwtPayload; } export function verifyRefreshToken(token: string): JwtPayload { return jwt.verify(token, config.JWT_REFRESH_SECRET) as JwtPayload; }3.2 认证中间件// src/middleware/auth.ts import { Request, Response, NextFunction } from "express"; import { verifyAccessToken } from "../utils/jwt"; // 扩展 Request 类型 declare global { namespace Express { interface Request { user?: { userId: string; role: string }; } } } // 基本认证 export function authRequired(req: Request, res: Response, next: NextFunction) { const authHeader = req.headers.authorization; if (!authHeader?.startsWith("Bearer ")) { return res.status(401).json({ code: 401, msg: "未提供认证令牌" }); } const token = authHeader.substring(7); try { const payload = verifyAccessToken(token); req.user = { userId: payload.userId, role: payload.role }; next(); } catch { return res.status(401).json({ code: 401, msg: "令牌无效或已过期" }); } } // 角色权限控制 export function requireRole(...roles: string[]) { return (req: Request, res: Response, next: NextFunction) => { if (!req.user) { return res.status(401).json({ code: 401, msg: "未认证" }); } if (!roles.includes(req.user.role)) { return res.status(403).json({ code: 403, msg: "权限不足" }); } next(); }; }3.3 路由实现// src/routes/auth.ts import { Router, Response } from "express"; import bcrypt from "bcryptjs"; import { authRequired, requireRole } from "../middleware/auth"; import { signAccessToken, signRefreshToken, verifyRefreshToken } from "../utils/jwt"; const router = Router(); // 模拟用户存储(实际应使用数据库) const users: Map<string, { id: string; username: string; password: string; role: string }> = new Map(); const refreshTokens: Set<string> = new Set(); // 注册 router.post("/register", async (req, res: Response) => { const { username, password } = req.body; if (!username || !password) { return res.status(400).json({ code: 400, msg: "用户名和密码不能为空" }); } if (users.has(username)) { return res.status(409).json({ code: 409, msg: "用户已存在" }); } const hashedPassword = await bcrypt.hash(password, 10); const id = Date.now().toString(); users.set(username, { id, username, password: hashedPassword, role: "user" }); res.json({ code: 0, msg: "注册成功" }); }); // 登录 router.post("/login", async (req, res: Response) => { const { username, password } = req.body; const user = users.get(username); if (!user) { return res.status(404).json({ code: 404, msg: "用户不存在" }); } const valid = await bcrypt.compare(password, user.password); if (!valid) { return res.status(401).json({ code: 401, msg: "密码错误" }); } const payload = { userId: user.id, role: user.role }; const accessToken = signAccessToken(payload); const refreshToken = signRefreshToken(payload); refreshTokens.add(refreshToken); res.json({ code: 0, msg: "登录成功", data: { accessToken, refreshToken, user: { id: user.id, username: user.username, role: user.role } }, }); }); // 刷新 Token router.post("/refresh", (req, res: Response) => { const { refreshToken } = req.body; if (!refreshToken || !refreshTokens.has(refreshToken)) { return res.status(401).json({ code: 401, msg: "无效的刷新令牌" }); } try { const payload = verifyRefreshToken(refreshToken); refreshTokens.delete(refreshToken); const newPayload = { userId: payload.userId, role: payload.role }; const newAccessToken = signAccessToken(newPayload); const newRefreshToken = signRefreshToken(newPayload); refreshTokens.add(newRefreshToken); res.json({ code: 0, msg: "刷新成功", data: { accessToken: newAccessToken, refreshToken: newRefreshToken }, }); } catch { refreshTokens.delete(refreshToken); return res.status(401).json({ code: 401, msg: "刷新令牌已过期" }); } }); // 登出 router.post("/logout", (req, res: Response) => { const { refreshToken } = req.body; refreshTokens.delete(refreshToken); res.json({ code: 0, msg: "已登出" }); }); // 受保护的路由 router.get("/profile", authRequired, (req, res: Response) => { res.json({ code: 0, data: { userId: req.user!.userId, role: req.user!.role } }); }); // 管理员路由 router.get("/admin/users", authRequired, requireRole("admin"), (req, res: Response) => { const userList = Array.from(users.values()).map(u => ({ id: u.id, username: u.username, role: u.role, })); res.json({ code: 0, data: userList }); }); export default router;3.4 主应用// src/app.ts import express from "express"; import cors from "cors"; import dotenv from "dotenv"; import authRoutes from "./routes/auth"; dotenv.config(); const app = express(); app.use(cors()); app.use(express.json()); app.use("/api/auth", authRoutes); app.get("/", (_req, res) => { res.json({ msg: "JWT Auth API" }); }); app.listen(3000, () => { console.log("Server running on http://localhost:3000"); });四、安全最佳实践Access Token 短时效(15 分钟),Refresh Token 长时效(7 天)Refresh Token 一次性使用:每次刷新后旧 Token 失效HTTPS 传输:生产环境必须使用 HTTPS密码加密:使用 bcrypt,不要用 MD5/SHA1环境变量管理密钥:不要硬编码到代码中Token 黑名单:登出时将 Token 加入黑名单(Redis)总结JWT 认证方案无状态、易扩展,适合前后端分离架构。通过 Access Token + Refresh Token 双 Token 机制,可以在安全性和用户体验之间取得平衡。生产环境中建议将 Refresh Token 存储在 Redis 中,方便统一管理和吊销。
2026年03月02日
11 阅读
0 评论
0 点赞
2026-03-02
MySQL 慢查询优化实战:从 EXPLAIN 到索引调优
前言MySQL 慢查询是影响应用性能的常见瓶颈。本文从慢查询日志分析入手,结合 EXPLAIN 执行计划解读,系统讲解索引优化和 SQL 调优方法。一、慢查询日志1.1 开启慢查询日志-- 查看慢查询配置 SHOW VARIABLES LIKE "%slow_query%"; -- 开启慢查询日志 SET GLOBAL slow_query_log = ON; SET GLOBAL long_query_time = 1; -- 超过 1 秒的查询 SET GLOBAL slow_query_log_file = "/var/log/mysql/slow.log"; -- 记录未使用索引的查询 SET GLOBAL log_queries_not_using_indexes = ON;1.2 使用 mysqldumpslow 分析# 按平均查询时间排序,取前 10 mysqldumpslow -s t -t 10 /var/log/mysql/slow.log # 按总耗时排序 mysqldumpslow -s at -t 10 /var/log/mysql/slow.log # 按次数排序 mysqldumpslow -s c -t 10 /var/log/mysql/slow.log二、EXPLAIN 执行计划2.1 基本用法EXPLAIN SELECT * FROM orders WHERE user_id = 10086 AND status = "paid";2.2 关键字段解读字段说明重点关注type访问类型至少达到 range 级别,避免 ALLkey实际使用的索引不为 NULLrows预估扫描行数越小越好Extra附加信息避免 Using filesort 和 Using temporary2.3 type 类型(从好到差)system > const > eq_ref > ref > range > index > ALLconst:主键或唯一索引等值查询eq_ref:JOIN 时使用主键或唯一索引ref:非唯一索引等值查询range:索引范围查询(BETWEEN、>、<、IN)index:扫描整个索引树ALL:全表扫描,必须优化三、索引优化3.1 创建合适的索引-- 单列索引 CREATE INDEX idx_user_id ON orders(user_id); -- 联合索引(注意最左前缀原则) CREATE INDEX idx_user_status ON orders(user_id, status, created_at); -- 覆盖索引(查询字段都在索引中) CREATE INDEX idx_cover ON orders(user_id, status, amount);3.2 最左前缀原则-- 联合索引 (a, b, c) -- 能命中索引: SELECT * FROM t WHERE a = 1; SELECT * FROM t WHERE a = 1 AND b = 2; SELECT * FROM t WHERE a = 1 AND b = 2 AND c = 3; -- 不能命中索引: SELECT * FROM t WHERE b = 2; SELECT * FROM t WHERE c = 3; SELECT * FROM t WHERE b = 2 AND c = 3;3.3 避免索引失效-- 函数操作导致索引失效 SELECT * FROM orders WHERE YEAR(created_at) = 2026; -- 优化: SELECT * FROM orders WHERE created_at >= "2026-01-01" AND created_at < "2027-01-01"; -- 隐式类型转换 SELECT * FROM orders WHERE order_no = 10086; -- order_no 是 varchar -- 优化: SELECT * FROM orders WHERE order_no = "10086"; -- LIKE 前导通配符 SELECT * FROM users WHERE name LIKE "%张"; -- 优化:使用全文索引或调整业务逻辑 SELECT * FROM users WHERE name LIKE "张%"; -- OR 导致索引失效 SELECT * FROM orders WHERE user_id = 1 OR status = "paid"; -- 优化:使用 UNION ALL SELECT * FROM orders WHERE user_id = 1 UNION ALL SELECT * FROM orders WHERE status = "paid" AND user_id != 1;四、SQL 调优实战4.1 分页优化-- 慢:OFFSET 大时性能差 SELECT * FROM orders ORDER BY id LIMIT 1000000, 20; -- 优化方案 1:延迟关联 SELECT * FROM orders o INNER JOIN (SELECT id FROM orders ORDER BY id LIMIT 1000000, 20) t ON o.id = t.id; -- 优化方案 2:游标分页(记住上一页最后一条 ID) SELECT * FROM orders WHERE id > 1000000 ORDER BY id LIMIT 20;4.2 批量插入-- 慢:循环单条插入 INSERT INTO logs (msg) VALUES ("log1"); INSERT INTO logs (msg) VALUES ("log2"); -- 优化:批量插入 INSERT INTO logs (msg) VALUES ("log1"), ("log2"), ("log3");4.3 使用 Redis 缓存热点数据import redis import json r = redis.Redis(host="localhost", port=6379, db=0) def get_user(user_id): # 先查缓存 cache_key = f"user:{user_id}" cached = r.get(cache_key) if cached: return json.loads(cached) # 查数据库 user = db.query("SELECT * FROM users WHERE id = %s", user_id) if user: r.setex(cache_key, 3600, json.dumps(user)) # 缓存 1 小时 return user五、总结MySQL 慢查询优化的核心思路:先通过慢查询日志找到问题 SQL,再用 EXPLAIN 分析执行计划,最后通过添加索引、调整 SQL 写法、引入缓存等方式优化。记住:不是所有慢查询都需要加索引,有时调整业务逻辑比优化 SQL 更有效。
2026年03月02日
15 阅读
0 评论
0 点赞
2026-02-27
MySQL如何解决深度分页问题?
在 MySQL 开发中,除了计数场景,深度分页(即 LIMIT offset, size 中 offset 值极大的分页查询)也是高频遇到的性能问题。比如"查询第1000页数据,每页10条"(LIMIT 9990, 10),常会出现查询缓慢的情况。下面详细讲解深度分页的问题根源及解决方案。1. 深度分页的问题根源先明确常规分页的实现方式:使用 LIMIT offset, size,其中 offset 表示跳过前 N 条数据,size 表示取 N 条数据。问题核心: 当 offset 极大时(如 9990),MySQL 需要先扫描并跳过前 9990 条数据,再取 10 条数据。即使查询条件有索引,也需要遍历索引到第 9990 条位置,才能定位到目标数据,导致 IO 开销大、查询效率低。示例(低效查询):-- 查询第1000页数据,每页10条,offset=9990 SELECT id, name FROM test_table LIMIT 9990, 10; -- 问题:MySQL需扫描前9990条数据并跳过,仅返回最后10条,资源浪费严重2. 解决方案(按适用场景排序)方案1:基于主键/索引排序+条件过滤(推荐,适用大部分场景)核心思路: 利用主键或有序索引的连续性,通过 WHERE 条件直接定位到"上一页最后一条数据",替代 offset 跳过数据。要求查询结果必须按主键或有序索引字段排序。实现步骤:分页查询时,记录上一页最后一条数据的主键/索引字段值(如最后一条数据的 id=9990);下一页查询时,通过 WHERE 主键 > 上一页最后主键 直接过滤前 9990 条数据,再用 LIMIT size 取数。示例(优化后查询):-- 第1页查询(无offset,直接取前10条) SELECT id, name FROM test_table ORDER BY id ASC LIMIT 10; -- 记录第1页最后一条数据的id=10 -- 第2页查询(通过id>10过滤前10条,无需offset) SELECT id, name FROM test_table WHERE id > 10 ORDER BY id ASC LIMIT 10; -- 记录第2页最后一条id=20 -- 第1000页查询(直接过滤前9990条,效率极高) SELECT id, name FROM test_table WHERE id > 9990 ORDER BY id ASC LIMIT 10;优点: 利用索引快速定位,无需扫描跳过的数据,查询效率稳定且高效缺点: 仅支持"下一页"查询,不支持直接跳转到任意页(如直接跳第1000页,需知道第999页最后一条的 id)方案2:游标分页(适合滚动加载场景)核心思路: MySQL 的 CURSOR(游标)本质是一个指向查询结果集的指针,可通过游标逐页读取数据,避免 offset 的低效扫描。适合"滚动加载更多"(如 APP 下拉加载)的场景,不支持跳页。实现步骤:-- 1. 声明游标(指定查询条件和排序方式) DECLARE cur_page CURSOR FOR SELECT id, name FROM test_table ORDER BY id ASC; -- 2. 打开游标 OPEN cur_page; -- 3. 读取数据(每次读取10条,即一页) FETCH cur_page INTO @id, @name; -- 逐行读取,可循环读取10条作为一页 -- 4. 关闭游标 CLOSE cur_page;优点: 适合大量数据的滚动加载,效率稳定缺点: 不支持直接跳页,语法较复杂,需在存储过程中使用方案3:预计算汇总表(适合静态/低频更新数据)核心思路: 如果数据是静态的(如历史订单、归档数据)或更新频率极低,可提前预计算分页所需的"分页标记"(如每10条数据的最大主键),存储在汇总表中。查询时直接从汇总表获取目标页的标记,再查询主表数据。实现示例:-- 1. 创建汇总表(存储每页最后一条数据的id和页码) CREATE TABLE page_summary ( page_num INT PRIMARY KEY, -- 页码 last_id INT -- 该页最后一条数据的id ); -- 2. 预插入数据(可通过定时任务更新) INSERT INTO page_summary VALUES (1, 10), (2, 20), ..., (1000, 10000); -- 3. 查询第1000页数据(先查汇总表,再查主表) SELECT last_id FROM page_summary WHERE page_num = 999; -- 得到第999页最后id=9990 SELECT id, name FROM test_table WHERE id > 9990 ORDER BY id ASC LIMIT 10;优点: 查询速度极快,适合大数据量静态数据缺点: 不适合动态更新数据,需维护汇总表(增加存储和维护成本)方案4:限制分页深度(业务层面优化)核心思路: 从业务角度限制用户的分页深度,避免用户查询过深的页码。比如"最多支持查询前100页数据",超过则提示"数据过多,请缩小查询范围"。实现示例:-- 限制offset最大值为9990(即最多100页) SELECT id, name FROM test_table WHERE offset <= 9990 LIMIT #{offset}, #{size};优点: 简单易实现,从根源减少深度分页查询缺点: 有业务局限性,不适用于必须支持深分页的场景(如后台数据导出)3. 方案选择总结方案适用场景核心优势局限性主键/索引条件过滤大部分动态数据分页(如列表查询)效率高、实现简单、兼容性好不支持直接跳页游标分页APP滚动加载、大量数据逐页读取效率稳定、适合大数据量不支持跳页、语法复杂预计算汇总表静态/低频更新数据(如归档、报表)查询速度极快需维护汇总表、不支持动态数据限制分页深度业务允许限制分页范围(如前台列表)实现简单、无性能开销业务局限性大补充提示: 深度分页优化的核心是"避免扫描跳过无关数据",无论哪种方案,都需确保查询条件中的排序字段有索引(如主键默认有索引),否则仍会出现全表扫描,优化失效。
2026年02月27日
45 阅读
0 评论
0 点赞
2026-02-27
MYSQL和POSTGRESQL怎么选?看完这篇就懂了
先搞懂:MySQL和PostgreSQL的核心差异如果把数据库比作"工具箱",MySQL像个"经济适用的老伙计"——够用、稳定、上手快,适合大多数基础需求;而PostgreSQL更像个"全能工程师",功能更灵活、扩展性更强,适合想"玩出花"的场景。具体差异主要在三点:1. 数据类型:灵活度差了一个LEVELMySQL的字段类型比较"保守",常用的VARCHAR、INT、DATETIME都能搞定,但遇到复杂需求(比如存JSON格式的标签、数组类型的分类)就有点吃力——虽然MySQL 5.7后支持了JSON字段,但本质是存文本,查询效率低,还没法直接对JSON里的内容建索引。PostgreSQL则像个"数据百宝箱":除了基础类型,它原生支持JSONB(二进制优化的JSON,能直接索引)、ARRAY(数组)、RANGE(时间/数值范围)甚至地理信息类型(GEOMETRY)。Halo博客里常见的"自定义文章元数据"(比如给文章打多个标签、存阅读量趋势),用PostgreSQL的JSONB存,查询时直接->>取字段,效率比MySQL高很多。2. 并发性能:多用户协作更丝滑博客最怕啥?博主和协作者同时改文章,结果"谁的版本覆盖了谁"。这涉及到数据库的"并发控制"。MySQL靠"行锁"解决冲突:你改一行,这行就被锁住,其他人得等。高并发下容易卡成"排队改稿"。PostgreSQL用的是"MVCC"(多版本并发控制):你改数据时,系统自动存一个旧版本;别人读的时候直接读旧版本,你改完再替换。读不挡写,写不挡读,多人同时编辑Halo博客,几乎感受不到延迟。3. 扩展能力:POSTGRESQL更"能折腾"Halo的魅力在于"可定制"——从主题到插件,很多人会想加新功能(比如给文章加地理位置标签、用图表统计阅读量)。这时候数据库的"扩展性"就关键了。MySQL的扩展主要靠插件,但数量少且功能集中(比如慢查询监控);PostgreSQL的插件生态像"应用商店":地理信息插件PostGIS、全文检索增强插件zhparser(中文分词)、甚至机器学习插件都能装。Halo如果后期想加"附近文章推荐""智能标签"这类功能,PostgreSQL几乎能无缝支持。Halo为啥更推荐PostgreSQL?回到Halo本身,它是个"轻量但长情"的博客系统——你可能从个人记录用到团队协作,从几篇文章写成几百篇。这时候PostgreSQL的优势会更明显:自定义字段友好:Halo支持给文章加自定义字段(比如"来源""阅读难度"),用PostgreSQL的JSONB存这些字段,既能灵活扩展,又能高效查询(比如按"阅读难度=中等"筛选文章)。多用户协作不卡顿:博主+编辑+评论管理员的多角色场景,PostgreSQL的MVCC能避免"改稿打架",编辑体验更流畅。长期稳定有保障:PostgreSQL的ACID事务更严格(比如转账操作"要么全成功,要么全回滚"),数据一致性比MySQL更可靠。毕竟博客数据是"数字资产",稳定比"够用"更重要。总结:按需选,但HALO更适配POSTGRESQL如果是纯个人博客,文章少、功能简单,MySQL完全够用(毕竟安装配置更省事);但如果想让Halo"长大"——加自定义功能、多人协作、未来扩展,PostgreSQL的灵活度和稳定性会更适配。毕竟,博客是"写给未来的自己"的,选个能陪你"折腾"的数据库,才不算辜负那些深夜敲字的灵感呀~(注:Halo官方文档对两种数据库均支持,但社区反馈PostgreSQL在复杂场景下表现更优,可根据需求选择。)
2026年02月27日
17 阅读
0 评论
0 点赞
2026-02-27
网站被 CC 攻击了?别慌,教你几招接地气的防护办法
今天本来想安安静静写个教程的,结果早上起来发现博客访问慢得像老牛拉破车。打开后台一看,好家伙,CC 攻击来了,不要问我哈,我也想知道为啥偏偏挑我这个小破站下手。咱这博客一共就 800 来篇文章,每天正常访问也就几百 IP,也不知道攻击者是咋想的,难道是想测试一下我的服务器抗不抗揍?哈哈哈~~~先说说啥是 CC 攻击吧通俗易懂的解释就是:有一群"机器人"假装成正常用户,不停地刷新你的网站,把你的服务器资源耗光,让真正想访问的人打不开网站。就像你开个小卖部,来了一群人只逛不买,还把过道堵死了,真正想买东西的顾客进不来,是不是这个理儿?我这个"栗子"是不是很恰当,哈哈哈~~~怎么确定是被攻击了?网站访问变慢了,平时打开页面 1 秒以内,今天直接 5 秒+甚至更久。这时就不用怀疑了。还有就是服务器 CPU 飙升,如果你平台有查看运行状态的好习惯就能看出来,毕竟平时 10% 左右,今天直接 90%+,是吧。还可以参考web日志里全是重复请求,这个就需要你回看日志了,打开 access.log 一看,好家伙,同一个 IP 几秒钟请求几百次,不会看也没关系,访问变慢就够了哈。我的应对办法(亲测有效)上 CDN(免费!)这个真的是神器,免费的 CDN 还能防 CC。配置也简单:去 cloudflare.com 注册个账号(不要钱)把你的域名 DNS 改到他们家开启"Under Attack Mode"(攻击模式)开启之后,访问网站会先验证一下是不是真人,机器人基本就拦住了。缺点:国内访问速度可能会慢一丢丢,但总比打不开强吧?当然了,你可以使用国内的CDN,比如腾讯云和阿里云等厂商的"边缘加速",效果很是牛,但是,对嘛肯定有但是的啊,域名必须备案,否则不行。我就在用,你可能会问那为什么用了还被攻击呢?呵呵,你猜呢?有没有可能我正在调试页面代码,不得已暂时关闭了CDN然后恰巧,被攻击了呢?哈哈,编剧都不敢这么写剧本哈!但是我就遇上了。。。WEB服务器 NGINX 限流如果你用的是 Nginx,加上这个配置:当然了如果你不会代码,还是别改了,毕竟配置文件差一个标点都可能崩溃,如果你实在想折腾下,就做好备份吧。# 限制每个 IP 每秒最多 10 个请求 limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s; # 在 server 块里应用 location / { limit_req zone=one burst=20 nodelay; # 其他配置... }这段话的意思是:每个 IP 每秒最多允许 10 次请求,超过的就排队,排不上的直接拒绝。可以设置的宽松一点,另外如果你选择了 CDN 那就可以忽略这个配置了,现在的 CDN 基本都有免费的防护,毕竟你能想到了,人家是专业的早就想到了。注意:别设置太严格,不然正常用户也可能被误伤,那就尴尬了。直接封 IP如果攻击规模不大,可以直接在防火墙封掉缺点:攻击者可以换 IP,治标不治本。但是能简单的防一防,仅仅是基本的哈~还是那句话,上了 CDN 基本都解决了,除非大流量攻击,这个是没有办法的需要花费"亿点点"的费用。毕竟免费的流量有限制哈~
2026年02月27日
21 阅读
0 评论
0 点赞
1
...
11
12
13
...
15
0:00