Java Spring项目中如何为Web URL创建安全短deeplink
Java Spring 项目高安全性短 Deeplink 实现方案
核心设计逻辑
要同时满足短链易传播、高安全两个要求,核心是把「短码生成-存储-解析跳转」全链路的风险点堵上,不要留可被利用的漏洞,同时控制短码长度在6-8位,适配短信、公众号等消息渠道的展示要求。
具体实现步骤
1. 短码生成与存储设计
- 短码不要用纯自增ID转Base62的方案,这种生成的短码是连续的,很容易被人遍历爬取所有有效链接。推荐用雪花算法分布式ID + 安全随机扰动的生成逻辑:取雪花ID的前4位转Base62编码作为基础位,再拼接2-3位由
SecureRandom生成的随机字符,最后打乱所有字符顺序,最终生成7位左右的短码即可,单码段可承载百亿级映射,完全够业务使用,且没有连续规律,很难被批量遍历。 - 存储采用Redis+MySQL双层架构:热点短码映射存在Redis,根据业务场景设置过期时间(比如通知类链接一般设7-30天有效期,到期自动删除),全量映射数据落MySQL持久化,表核心字段只需要存短码、原始目标地址、创建时间、过期时间、绑定用户ID(可选)、风险标记即可,不要冗余无关字段。
- 注意:不要把原始URL的任何特征编码进短码,避免被反向破解出原始地址规则。
2. 全链路安全防护(核心风险规避)
- 原始域名白名单校验:所有申请生成短链的原始URL,必须提前在系统配置域名白名单,只允许生成指向自有业务域名的短链,非白名单地址直接拒绝生成,从源头堵死短链被用来跳转钓鱼、违规站点的风险。
- 接口防爬策略:短链解析接口加IP访问频率限制,单IP1分钟内请求超过阈值(比如20次)直接返回429拦截,避免攻击者批量枚举有效短码。
- 专属链接权限校验:如果是发送给指定用户的专属链接(比如账单、活动邀请链接),生成短链时绑定目标用户ID,解析时校验当前访问者的登录态/携带的用户凭证,和绑定用户不匹配直接返回403,避免链接泄露后被无关人员访问。
- 风险熔断机制:建立短链举报、风险标记通道,一旦某条短链被标记为违规,直接在解析层拦截,跳转到内部风险提示页,不再做原始地址跳转。
3. Spring 框架落地核心代码
短码生成工具类示例:
import java.security.SecureRandom; public class ShortCodeUtil { // Base62编码字符集,可去掉容易混淆的l、I、O、0等字符降低用户输入错误率 private static final String BASE_CHARS = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"; private static final int CODE_LEN = 7; private static final SecureRandom SECURE_RANDOM = new SecureRandom(); public static String genCode() { StringBuilder codeBuilder = new StringBuilder(); // 取雪花ID转Base62的前4位作为基础位 long snowId = SnowflakeIdWorker.nextId(); String idSeg = base62Encode(snowId).substring(0, 4); codeBuilder.append(idSeg); // 拼接3位随机扰动位 for (int i = 0; i < CODE_LEN - 4; i++) { codeBuilder.append(BASE_CHARS.charAt(SECURE_RANDOM.nextInt(BASE_CHARS.length()))); } // 打乱字符顺序,隐藏ID段规律 return shuffleString(codeBuilder.toString()); } // 省略base62Encode、shuffleString内部实现逻辑 }
短链解析接口示例:
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Date; @RestController public class DeeplinkResolveController { private final ShortLinkService shortLinkService; // 构造注入省略 @GetMapping("/dl/{code}") public void resolve(@PathVariable String code, HttpServletRequest request, HttpServletResponse response) throws Exception { // IP频率校验 String clientIp = IpUtil.getClientIp(request); if (!rateLimitService.allowAccess(clientIp)) { response.setStatus(429); return; } // 查询短链信息 ShortLinkDO linkInfo = shortLinkService.getByCode(code); // 短链不存在或已过期返回404 if (linkInfo == null || linkInfo.getExpireTime().before(new Date())) { response.setStatus(404); return; } // 二次校验目标域名在白名单内 if (!DomainWhiteListUtil.match(linkInfo.getTargetUrl())) { response.setStatus(403); return; } // 专属链接用户身份校验,按需开启 // if (!userAuthService.checkMatch(linkInfo.getBindUserId(), request)) { response.setStatus(403); return; } // 按需加UA判断:移动端访问优先唤起App,实现deeplink能力 // if (UserAgentUtil.isMobile(request)) { response.sendRedirect(linkInfo.getAppScheme()); return; } // 302跳转到目标地址 response.sendRedirect(linkInfo.getTargetUrl()); } }
4. 落地优化建议
- 短链尽量用独立的短域名,长度越短越好,不要用业务主域名,避免短链出现风险时牵连主域名的信用评级。
- 所有短链访问日志全量留存,记录访问IP、UA、访问时间,出现安全问题可快速溯源。
- 非必要不生成永久有效短链,根据业务场景设置合理过期时间,减少存量风险。
内容的提问来源于stack exchange,提问作者AnilF
相关产品推荐
相关产品推荐

