秒杀接口隐藏解决思路
秒杀接口隐藏
思路
- 秒杀开始后,点击秒杀按钮不是直接进行秒杀,而是获取秒杀的接口地址。
- 每个人获得的秒杀地址都不一样。
- 获取到秒杀接口地址后,在进行秒杀操作
- 这种操作如何防止脚本?
- 秒杀的接口并不是真正的秒杀接口,即使脚本知道这个秒杀接口也无法进行秒杀,时间到了才能获取到地址,并且地址具有一分钟的失效时间,等获取到了这个地址,添加进脚本的时候,已经被抢光了。
- 优化前 秒杀接口->秒杀操作
- 优化后 秒杀接口->获取唯一秒杀地址->秒杀地址拼接->秒杀请求发送->秒杀操作
- 能防止一部分脚本,但是有些能自动拼接的脚本比较麻烦,可以使用验证码。
前端操作
- 秒杀按钮点击调用获取秒杀路径的接口path
- 将获取到的path拼接到请求中
后端操作
Controller层
1
2
3
4
5
6
7
8
9
10
public RespBean getPath(TUser tuser, Long goodsId) {
if (tuser == null) {
return RespBean.error(RespBeanEnum.SESSION_ERROR);
}
String str = orderService.createPath(tuser, goodsId);
return RespBean.success(str);
}秒杀功能接口地址改为/{path}/doSeckill,用@PathVariable获取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public RespBean doSecKill( String path, TUser user, Long goodsId){
if (user == null) {
return RespBean.error(RespBeanEnum.SESSION_ERROR);
}
//优化后代码
ValueOperations valueOperations = redisTemplate.opsForValue();
//如果传入路径redis里没有就返回请求非法
boolean check = orderService.checkPath(user, goodsId, path);
if (!check) {
return RespBean.error(RespBeanEnum.REQUEST_ILLEGAL);
}
//以下省略
}
Service层
生成秒杀地址
1
2
3
4
5
6
7
public String createPath(TUser user, Long goodsId) {
//随机生成一串UUID并加密,根据用户和商品生成路径信息存入redis,设置1分钟失效
String str = MD5Util.md5(UUIDUtil.uuid() + "123456");
redisTemplate.opsForValue().set("seckillPath:" + user.getId() + ":" + goodsId, str, 1, TimeUnit.MINUTES);
return str;
}检查秒杀地址
1
2
3
4
5
6
7
8
public boolean checkPath(TUser user, Long goodsId, String path) {
if (user == null || goodsId < 0 || StringUtils.isEmpty(path)) {
return false;
}
String redisPath = (String) redisTemplate.opsForValue().get("seckillPath:" + user.getId() + ":" + goodsId);
return path.equals(redisPath);
}
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Katashi的博客!