文章只展示部分核心代码,完整代码请参考源码


    一、秒杀接口地址隐藏
    思路:秒杀开始前,先请求接口获取秒杀地址
    1.接口改造,带上PathVariable参数
    前端页面请求:

    1. function getMiaoshaPath() {
    2. var goodsId = $("#goodsId").val();
    3. $.ajax({
    4. url:"/miaosha/path",
    5. type:"GET",
    6. data:{
    7. goodsId:goodsId
    8. },
    9. success:function(data){
    10. if(data.code == 0){
    11. var path = data.data;
    12. doMiaosha(path);
    13. }else{
    14. layer.msg(data.msg);
    15. }
    16. },
    17. error:function(){
    18. layer.msg("客户端请求有误");
    19. }
    20. });
    21. }

    2.添加生成地址的接口
    Controller 层:创建一个临时path值,保存到redis缓存中,并设置过期时间

    1. @RequestMapping(value="/path", method=RequestMethod.GET)
    2. @ResponseBody
    3. public Result<String> getMiaoshaPath(Model model, MiaoshaUser user,
    4. @RequestParam("goodsId")long goodsId) {
    5. model.addAttribute("user", user);
    6. if (user == null) {
    7. return Result.error(CodeMsg.SESSION_ERROR);
    8. }
    9. String path = miaoshaService.createMiaoshaPath(user,goodsId);
    10. return Result.success(path);
    11. }
    12. //dao
    13. public String createMiaoshaPath(MiaoshaUser user, long goodsId) {
    14. String str = MD5Util.md5(UUIDUtil.uuid()+"123456");
    15. redisService.set(MiaoshaKey.getMiaoshaPath,""+user.getId()+"_"+goodsId,str);
    16. return str;
    17. }

    3.接口收到请求,先验证PathVariable
    秒杀前先验证path,从redis缓存从取出

    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);
    }
    

    二、数学公式验证码

    • 生成验证码
      @RequestMapping(value="/verifyCode", method=RequestMethod.GET)
      @ResponseBody
      public Result<String> getMiaoshaVerifyCode(HttpServletResponse response,Model model, MiaoshaUser user,
                                               @RequestParam("goodsId")long goodsId) {
        model.addAttribute("user", user);
        if (user == null) {
            return Result.error(CodeMsg.SESSION_ERROR);
        }
        BufferedImage image = miaoshaService.createVerifyCode(user,goodsId);
        try {
            OutputStream out = response.getOutputStream();
            ImageIO.write(image,"JPEG",out);
            out.flush();
            out.close();
            return null;
        }catch (Exception e){
            return Result.error(CodeMsg.MIAOSHA_FAIL);
        }
      }
      
      ```java public BufferedImage createVerifyCode(MiaoshaUser user, long goodsId) { if (user == null || goodsId <= 0) {
        return null;
      
      } int width = 80; int height = 32; //create the image BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics g = image.getGraphics(); // set the background color g.setColor(new Color(0xDCDCDC)); g.fillRect(0, 0, width, height); // draw the border g.setColor(Color.black); g.drawRect(0, 0, width - 1, height - 1); // create a random instance to generate the codes Random rdm = new Random(); // make some confusion for (int i = 0; i < 50; i++) {
        int x = rdm.nextInt(width);
        int y = rdm.nextInt(height);
        g.drawOval(x, y, 0, 0);
      
      } // generate a random code String verifyCode = generateVerifyCode(rdm); g.setColor(new Color(0, 100, 0)); g.setFont(new Font(“Candara”, Font.BOLD, 24)); g.drawString(verifyCode, 8, 24); g.dispose(); //把验证码存到redis中 int rnd = calc(verifyCode); redisService.set(MiaoshaKey.getMiaoshaVerifyCode, user.getId() + “,” + goodsId, rnd); //输出图片 return image; }

    private int calc(String exp) { try{ ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName(“JavaScript”); return (Integer)engine.eval(exp); }catch (Exception e){ e.printStackTrace(); return 0; } }

    private static char[] ops = new char[]{‘+’, ‘-‘, ‘*’};

    private String generateVerifyCode(Random rdm) { int num1 = rdm.nextInt(10); int num2 = rdm.nextInt(10); int num3 = rdm.nextInt(10); char op1 = ops[rdm.nextInt(3)]; char op2 = ops[rdm.nextInt(3)]; String exp = “” + num1 + op1 + num2 + op2 + num3; return exp; }

    
    - 校验验证码
    ```java
    public boolean checkVerifyCode(MiaoshaUser user, long goodsId, int verifyCode) {
        if (user ==null || goodsId  <= 0){
            return false;
        }
        Integer codeOld = redisService.get(MiaoshaKey.getMiaoshaVerifyCode, user.getId() + "," + goodsId, Integer.class);
        if (codeOld == null || codeOld-verifyCode != 0){
            return false;
        }
        redisService.delete(MiaoshaKey.getMiaoshaVerifyCode,user.getId() + "," + goodsId);
        return true;
    }
    
    • 前端发起请求

      var verifyCode = $("#verifyCode").val();
        $.ajax({
            url:"/miaosha/path",
            type:"GET",
            data:{
                goodsId:goodsId,
                verifyCode:verifyCode
            },
      

      三、接口限流防刷

    • 自定义一个注解实现对接口方法进行拦截

      @AccessLimit(seconds=10,maxCount=5,needLogin=true)
      
    • 具体实现步骤

      @Retention(RUNTIME)
      @Target(METHOD)
      public @interface AccessLimit {
        int seconds();
        int maxCount();
        boolean needLogin() default true;
      }
      

      ```java @Service public class AccessInterceptor extends HandlerInterceptorAdapter {

      @Autowired MiaoshaUserService userService;

      @Autowired RedisService redisService;

      @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        if (handler instanceof HandlerMethod){
            MiaoshaUser user = getUser(request, response);
            UserContext.setUser(user);
      
            HandlerMethod hm = (HandlerMethod) handler;
            AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
            if (accessLimit == null){
                return true;
            }
            int seconds = accessLimit.seconds();
            int macCount = accessLimit.maxCount();
            boolean needLogin = accessLimit.needLogin();
            String key = request.getRequestURI();
            if (needLogin) {
                if (user == null){
                    render(response,CodeMsg.SESSION_ERROR);
                    return false;
                }
                key += "_"+user.getId();
            }else {
                //do nothing
            }
            AccessKey accessKey = AccessKey.withExpire(seconds);
            Integer count = redisService.get(accessKey, key, Integer.class);
            if (count == null){
                redisService.set(accessKey,key,1);
            }else if (count < macCount){
                redisService.incr(accessKey,key);
            }else {
                render(response,CodeMsg.ACCESS_LIMIT_REACHED);
                return false;
            }
        }
        return true;
      

      }

      private void render(HttpServletResponse response, CodeMsg codeMsg) throws Exception {

        response.setContentType("application/json;charset=UTF-8");
        OutputStream out = response.getOutputStream();
        String str = JSON.toJSONString(Result.error(codeMsg));
        out.write(str.getBytes("UTF-8"));
        out.flush();
        out.close();
      

      }

      private MiaoshaUser getUser(HttpServletRequest request, HttpServletResponse response){

        String paramToken = request.getParameter(MiaoshaUserService.COOKI_NAME_TOKEN);
        String cookieToken = getCookieValue(request, MiaoshaUserService.COOKI_NAME_TOKEN);
        if(StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)) {
            return null;
        }
        String token = StringUtils.isEmpty(paramToken)?cookieToken:paramToken;
        return userService.getByToken(response, token);
      

      }

    private String getCookieValue(HttpServletRequest request, String cookiName) {
        Cookie[]  cookies = request.getCookies();
        if(cookies == null || cookies.length <= 0){
            return null;
        }
        for(Cookie cookie : cookies) {
            if(cookie.getName().equals(cookiName)) {
                return cookie.getValue();
            }
        }
        return null;
    }
    

    }

    
    - 用 ThreadLocal 保存 user 
    ```java
    public class UserContext {
    
        private static ThreadLocal<MiaoshaUser> userHolder = new ThreadLocal<MiaoshaUser>();
    
        public static void setUser(MiaoshaUser user){
            userHolder.set(user);
        }
    
        public static MiaoshaUser getUser(){
            return userHolder.get();
        }
    }
    
    • 在 WebConfig 中将接口注册
      @Override
      public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(accessInterceptor);
      }