TP6.0中用自增ID转62进制生成短码最稳妥可逆无碰撞,需手写编码函数、起始ID设为1000,跳转时严格校验路径段并忽略查询参数,查库失败或非法协议返回404,跳转后必须exit,访问统计与Redis风控结合实现。

用自增ID + 62进制转换生成短码
这是TP6.0中最稳妥、可逆、无碰撞的生成方式。不依赖哈希或随机,天然适配数据库主键。核心是把数据库插入后返回的id(如12345)转成62进制字符串(如3dR),字符集为0-9a-zA-Z共62个字符。
别用base_convert()——它只支持到36进制。需手写编码函数,逻辑是不断对62取余、整除,拼接对应字符。起始ID建议设为AUTO_INCREMENT = 1000,避免生成单字符(如"1"、"a")导致长度不可控。
示例实现要点:
- 建表字段:
id (BIGINT AUTO_INCREMENT)、long_url (TEXT)、created_at (DATETIME) - 给
long_url加唯一索引:ALTER TABLE short_urls ADD UNIQUE INDEX idx_long_url (long_url(255)) - 插入时用
INSERT ... ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id),再调$this->db->getLastInsID()获取真实ID
跳转逻辑必须严格校验路径段
用户访问/go/3dR时,TP6.0应从PATH_INFO中提取3dR,**完全忽略查询参数**。这是防开放重定向漏洞的关键。
若请求带?to=https://evil.com,直接丢弃,不参与任何跳转判断。查库失败、long_url为空、或协议非法(如不含http://或https://,且非白名单内网地址),统一返回404,不 fallback、不输出错误信息。
跳转代码必须简洁明确:
header('Location: ' . $url);-
exit;—— 漏掉exit会导致后续代码执行,可能泄露日志或重复统计 - 禁用
display_errors,捕获所有异常并记录,不暴露数据库结构或路径
轻量级访问统计与风控基础
每次跳转前,需记录一次访问元数据,但不能拖慢响应。推荐做法:将short_code、ip、ua_hash(md5(substr($ua,0,100)))、referer_host、created_at写入MySQL的access_log表;高并发下可用INSERT IGNORE或先写本地日志文件,再由Logstash异步入库。
基础风控靠Redis实现:
- IP日频控:
INCR ip:192.168.1.100:20260709+EXPIRE,超50次返回429 - 短码日频控:
INCR short:3dR:20260709,超1000次暂停跳转并标记可疑 - IP刷链检测:同一IP 5分钟内请求≥20个不同短码,自动加入
black_ip_list,1小时过期
TP6.0路由与服务层组织建议
在app/route/app.php中定义简洁路由:
Route::get('go/<code>', 'index/ShortUrl/jump')->pattern(['code' => '\w{4,8}']);Route::post('api/shorten', 'index/ShortUrl/create');
业务逻辑收拢到app/service/ShortUrlService.php中,封装encode()(含去重+ID生成+62进制)、decode()(查库+校验)、logAccess()和checkRateLimit()方法。控制器只做参数接收与响应包装,保持轻量。
注意:不要用REPLACE INTO去重,它会触发DELETE+INSERT,在有外键或审计字段时行为不可控;ON DUPLICATE KEY UPDATE才是生产环境唯一可靠方案。


















