[shopping mall seckill project] - use mathematical graphic verification code to limit current

Posted by mikem562 on Wed, 26 Feb 2020 07:13:13 +0100

Seckill interface address hiding can prevent malicious users from calling the interface frequently to request operations, but it can't prevent robots. The ticketing software can still click the button frequently to request the seckill address interface maliciously

In the scenario of high concurrency, at the moment when the seconds are just killing, the concurrency is the largest. Reducing the concurrency at the same time point, and shunting the concurrency is also a measure to reduce the pressure on the database and the system (making 100000 requests in 1s transition to 100000 requests in 10s)

This blog records how to use the mathematical graphic verification code to limit the current. Please refer to:

[shopping mall seckill project] - how to cut peak flow

Implementation ideas:

Before clicking seckill, you need to input the verification code to disperse the user's request. The specific implementation is that the backend generates a verification code similar to 1 + 2-3, stores the result in redis, and then sends the verification code picture to the client. After that, the client enters the verification code value before requesting the seckill address and sends the request for verification. The backend goes to the cache to verify whether the value is the same as the value entered by the user. Only after the verification is passed can the seckill address be dynamically generated to the front end

Steps:

  • On the product details page, add the verification code image tag, specify the id, and then add the input component of the verification code input box, and initialize their attributes to be invisible. Because the verification code and input box are invisible at the beginning (only the second kill will be visible), the picture can be clicked to refresh the picture, so define the refreshVCode method to refresh the picture

  • In the countdown method, the code logic of displaying the verification code and the verification code input box is added to the seckill branch judgment. When the seckill starts, set it to be visible and specify the attr() method to dynamically specify src, send the request to the back end, and dynamically generate the picture. After the seckill, set it to be invisible

  • In the request, the parameter is goodsId, then the mathematical formula verification code is generated according to the user id and goodsId, and the verification code picture is output to the front end with the response output stream

Back end generate verification code image interface code:

@RequestMapping(value = "/verifyCode", method = RequestMethod.GET)
@ResponseBody
public Result<String> getMiaoshaVerifyCod(HttpServletResponse response,MiaoshaUser user,
                                          @RequestParam("goodsId") long goodsId) {
    if (user == null) {
        return Result.error(CodeMsg.SESSION_ERROR);
    }
    try {
        BufferedImage image = miaoshaService.createVerifyCode(user, goodsId);
        OutputStream out = response.getOutputStream();
        ImageIO.write(image, "JPEG", out);
        out.flush();
        out.close();
        return null;
    } catch (Exception e) {
        e.printStackTrace();
        return Result.error(CodeMsg.MIAOSHA_FAIL);
    }
}

The image is generated by BufferedImage class, which specifies the height and width, uses Graphics to draw a brush, fills in the color, draws the boundary line and other operations, then uses drawString method to randomly splice the verification code into a string to write on the generated image, and also stores the calculated string value in redis

createMiaoshaVertifyCode method code:

/**
 * Generate verification code
 */
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();
    //Save the verification code in redis
    int rnd = calc(verifyCode);
    redisService.set(MiaoshaKey.getMiaoshaVerifyCode, user.getId() + "," + goodsId, rnd);
    //Output pictures
    return image;
}

For the generation of mathematical formulas, the generateVerifyCode method is used to generate three random numbers between 0 and 9, and then generate a character array for storing + -* (addition, subtraction and multiplication) three mathematical operators, randomly select two characters, and then splice them into a string. The number + operator + number + operator + number returns this string. The code of generateVerifyCode method is as follows:

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

/**
 * The formula of generating addition and subtraction multiplication
 * + - *
 */
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;
}
  • Using the scriptEngine class, call the eval() method of JavaScript to calculate the value of the string formula, and save the value in redis (the next time the user sends the request, go to the cache directly to take it out and verify it); note that the eval() method can calculate the value of double type, but we need the value of int type, so we need to force the conversion

The calc method code is as follows:

/**
 * Calculate the formula
 */
private static 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;
    }
}
  • The front end gets the verification code picture to display the verification code, and then the user needs to input the verification code as a parameter, and transmit it to the back end together with the request to obtain the seckill address (the operation of verification is before obtaining the seckill address). After the back end receives the parameter, the verification code is compared, and the verification code is taken out of the cache for verification; if it fails, the seckill connection is not generated Port address, directly return the error information of verification code

Back end validation logic:

checkVerifyCode method code:

/**
 * Verification code result of verification number formula
 * @param user
 * @param goodsId
 * @param verifyCode
 * @return
 */
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;
    }
    //Delete data in cache
    redisService.delete(MiaoshaKey.getMiaoshaVerifyCode, user.getId() + "," + goodsId);
    return true;
}
132 original articles published, 92 praised, 30000 visitors+
Private letter follow

Topics: Redis Javascript Database