上期回顾: Java Seckill Module:Redis pre-reduced inventory and Rabbitmq asynchronous order
在服务端对系统做一层保护。
秒杀接口地址隐藏:在秒杀开始之前,地址不是写死的,而是从服务端获取动态生成一个地址,就是说秒杀开始之前是不知道访问哪一个地址的。
数学公式验证码:防止恶意用户刷秒杀接口,用机器人或者其他手段,并且输入验证码可以拉长了访问系统的时间,这是有效的削峰的情况。
接口限流防刷:做一个限制,例如一分钟限制用户最多访问系统十次,这样的限流手段,不是单单针对秒杀的应用场景,而是通用的
例如在页面秒杀按钮,前端已经做秒杀的限制,但是这种判断只是为了防止用户输入错误,用户体验的js逻辑,前端是http协议的,数据是明文的,地址以及传递的参数都能被拿到,如果是恶意的用户,就可能出现危险,所以对于安全的验证还是在服务端中进行的。
思路:秒杀开始之前,先去请求接口获取秒杀地址 1.接口改造,带上PathVariable参数 2.添加生成地址的接口 3.秒杀收到请求,先验证PathVariable
页面需要修改,不是直接去请求秒杀地址,而是先从服务端获取秒杀地址:
<button class="btn btn-primary" type="button" id="buyButton"οnclick="getMiaoshaPath()">立即秒杀</button> function getMiaoshaPath(){ var goodsId = $("#goodsId").val(); g_showLoading(); $.ajax({ url:"/miaosha/path", type:"GET", data:{ goodsId:goodsId, verifyCode:$("#verifyCode").val() }, success:function(data){ if(data.code == 0){ var path = data.data; doMiaosha(path); }else{ layer.msg(data.msg); } }, error:function(){ layer.msg("客户端请求有误"); } }); }解读:拿到秒杀Path之后,就进行doMiaosha(path):
function doMiaosha(path){ $.ajax({ url:"/miaosha/"+path+"/do_miaosha", type:"POST", data:{ goodsId:$("#goodsId").val() }, success:function(data){ if(data.code == 0){ getMiaoshaResult($("#goodsId").val()); }else{ layer.msg(data.msg); } }, error:function(){ layer.msg("客户端请求有误"); } }); }服务端秒杀接口中添加:
*****MiaoshaKey 秒杀地址有效期: public static MiaoshaKey getMiaoshaPath = new MiaoshaKey(60, "mp"); *****MiaoshaController:miaosha 收到path之后,就开始验证path: public Result<Integer> miaosha(Model model,MiaoshaUser user, @RequestParam("goodsId")long goodsId, @PathVariable("path") String path) { ...... boolean check = miaoshaService.checkPath(user, goodsId, path); if(!check){ return Result.error(CodeMsg.REQUEST_ILLEGAL); } *****MiaoshaService 验证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); } 解读:获取之前存储的path,如何进行比较是否相等。 *****MiaoshaController 秒杀地址接口: @RequestMapping(value="/path", method= RequestMethod.GET) @ResponseBody public Result<String> getMiaoshaPath(HttpServletRequest request, MiaoshaUser user, @RequestParam("goodsId")long goodsId, @RequestParam(value="verifyCode", defaultValue="0")int verifyCode) { if(user == null) { return Result.error(CodeMsg.SESSION_ERROR); } String path = miaoshaService.createMiaoshaPath(user, goodsId); return Result.success(path); } *****MiaoshaService 生成path: 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; } 解读:不同用户不同的商品,值是不一样的,同一个用户也是不一样的,这里用了uuid来随机生成,随后将path放入redis中。