秒杀功能(7)安全优化一

安全优化

从本篇开始讲秒杀系统的安全优化。主要分为三大块:

  1. 秒杀接口地址隐藏
  2. 数学公式验证码
  3. 接口限流防刷

秒杀接口地址隐藏

每次点击秒杀按钮,才会生成秒杀地址,之前是不知道秒杀地址的。不是写死的,是从服务端获取,动态拼接而成的地址。(Http协议是明文传输,透明的,前端无法控制恶意用户进行攻击)安全校验还是要放在服务端,禁止掉这些恶意服务。

该操作:可以为了防止,恶意用户登陆之后,获取token的情况下,通过不断调用秒杀地址接口,来达到刷单的恶意请求。

每次的url都不一样,只有真正点击秒杀按钮,才会根据商品和用户id生成对应的秒杀接口地址。

但是,这种情况仍然不能解决 利用 按键精灵或者 机器人 频繁点击按钮的操作,为了降低点击按钮的次数,以及高并发下,防止多个用户在同一时间内,并发出大量请求,加入数学公式图形验证码等防高并发优化。

思路:秒杀开始之前,先去请求接口获取秒杀地址。
流程:

  1. 在用户点击“立即秒杀”时先由前端发出请求到/miaosah/path路径;
  2. 添加生成地址的接口(/miaosah/path),后台通过UUID和随机值并用MD5加密生成真正的秒杀地址,并且在redis中存入用户id/商品id和秒杀地址的键值对(为了之后的验证使用,Redis的有效期设置为60s,因为跳转到秒杀页面非常快);
  3. do_miaosha接口改造,带上PathVariable参数,秒杀接口收到请求,先验证PathVariable和缓存中存的是否一致,若一致才进入秒杀环节。

MiaoshaController中改动的相关代码如下:

//第四版,秒杀接口地址隐藏
    @RequestMapping(value = "/{path}/do_miaosha", method = RequestMethod.POST)
    @ResponseBody
    public Result<Integer> miaosha(Model model, @RequestParam("goodsId") long goodsId, HttpServletResponse response,
                                   @CookieValue(value = MiaoshaUserService.COOKI_NAME_TOKEN,required = false) String cookieToken,
                                   @RequestParam(value = MiaoshaUserService.COOKI_NAME_TOKEN,required = false) String paramToken,
                                   @PathVariable("path") String path) {
        if (StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)) {
            return Result.error(CodeMsg.SESSION_ERROR);//token不存在或失效
        }
        String token = StringUtils.isEmpty(paramToken) ? cookieToken : paramToken;
        MiaoshaUser user = userService.getByToken(response, token);//从token中读用户信息

        //验证path
        boolean check = miaoshaService.checkPath(user, goodsId, path);
        if (!check) {
            return Result.error(CodeMsg.REQUEST_ILLEGAL);
        }

        //内存标记,减少redis访问
        boolean over = localOverMap.get(goodsId);
        if (over) {//库存减去10后不必要访问redis,访问map即可
            return Result.error(CodeMsg.MIAO_SHA_OVER);
        }
        //预减库存
        long stock = redisService.decr(GoodsKey.getMiaoshaGoodsStock, "" + goodsId);
        if (stock < 0) {
            localOverMap.put(goodsId, true);
            return Result.error(CodeMsg.MIAO_SHA_OVER);
        }
        //判断是否已经秒杀到了
        MiaoshaOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId);
        if (order != null) {
            return Result.error(CodeMsg.REPEATE_MIAOSHA);
        }
        //入队
        MiaoshaMessage mm = new MiaoshaMessage();
        mm.setUser(user);
        mm.setGoodsId(goodsId);
        sender.sendMiaoshaMessage(mm);
        return Result.success(0);//排队中
    }

    @RequestMapping(value = "/path", method = RequestMethod.GET)
    @ResponseBody
    public Result<String> getMiaoshaPath(HttpServletResponse response, @RequestParam("goodsId") long goodsId,
                                         @CookieValue(value = MiaoshaUserService.COOKI_NAME_TOKEN,required = false) String cookieToken,
                                         @RequestParam(value = MiaoshaUserService.COOKI_NAME_TOKEN,required = false) String paramToken) {
        if (StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)) {
            return Result.error(CodeMsg.SESSION_ERROR);//token不存在或失效
        }
        String token = StringUtils.isEmpty(paramToken) ? cookieToken : paramToken;
        MiaoshaUser user = userService.getByToken(response, token);//从token中读用户信息

        String path = miaoshaService.createMiaoshaPath(user, goodsId);
        return Result.success(path);
    }

生成秒杀地址的代码和验证秒杀地址的代码如下:

	public boolean checkPath(MiaoshaUser user, long goodsId, String path) {
        if (user == null || path == null) {
            return false;
        }
        String pathOld = redisService.get(MiaoshaKey.getMiaoshaPath, "" + user.getId() + "_" + goodsId, String.class);
        return path.equals(pathOld);
    }

	public String createMiaoshaPath(MiaoshaUser user, long goodsId) {
        if (user == null || goodsId <= 0) {
            return null;
        }
        String str = MD5Util.md5(UUIDUtil.uuid() + "123456");
        redisService.set(MiaoshaKey.getMiaoshaPath, "" + user.getId() + "_" + goodsId, str);
        return str;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值