Getting Verification Code under Spring MVC

Keywords: Java Session Spring Redis

Let me start with some personal opinions on login authentication codes.

  1. Users are not advised to enter authentication codes when they first log in, which has been well demonstrated on many websites. Only when users input authentication codes three or more times in a row will users be required to enter authentication codes.
  2. It is better not to use session to record the number of user errors, because session is related to the client browser session. If you restart the browser or replace the browser to login or try again, it will be a new call, and the number of errors originally recorded will be invalid. Simple processing is to use Map< user login id, number of errors & gt; to achieve, if there are more than one server load, you need to use another caching mechanism, such as Redis.
  3. When the user enters the username, it needs to use the login name to judge whether the verification code is needed.
  4. Whether the generated verification code is bound to session or not needs to be checked depends on the number of failures within the specified time.

> Native servlets can write pictures directly to the client. But children's shoes just using Spring MVC may not know how to return pictures under this framework. In essence, returning pictures with spring is the same as using servlets. They all use HttpServletResponse to return images.

The Method of Obtaining Verification Code in Spring MVC controller

/**
     * Generating Verification Code
     * @param request
     * @param response
     */
    @RequestMapping(value = "login/getVerifyCode")
    public void getVerifyCode(HttpServletRequest request,HttpServletResponse response){
        response.setHeader("Pragma","No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        response.setContentType("image/jpeg");

        //Generate random strings
        String verifyCode = VerifyCodeUtils.generateVerifyCode(4);
        //Save it in Session, where you can customize it
        HttpSession session = request.getSession();
        session.setAttribute("verifyCode",verifyCode);
        //Generate pictures
        int w = 100, h = 35;
        try {
            //Write the picture to the output stream of response to return the picture to the client.
            VerifyCodeUtils.outputImage(w, h , response.getOutputStream(), verifyCode);
        } catch (IOException e) {
            logger.error("Failure to generate validation code, Cause by: {}", e.getMessage(), e);
        }
    }

Classes for Generating Verification Code Pictures

> Find one on the internet. It's not bad.

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Random;

/**
 * Generate picture streams
 * @Description: Created by zcqshine on 2017/5/18.
 */
public class VerifyCodeUtils {
    private static final String VERIFY_CODES = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
    private static Random random = new Random();

    /**
     * Generating Verification Code with System Default Character Source
     * @param verifySize    Verification code length
     * @return
     */
    public static String generateVerifyCode(int verifySize){
        return generateVerifyCode(verifySize, VERIFY_CODES);
    }

    /**
     * Generate authentication code with specified source
     * @param verifySize    Verification code length
     * @param sources       Verification Code Character Source
     * @return
     */
    public static String generateVerifyCode(int verifySize, String sources){
        if (sources == null || sources.trim().length() == 0){
            sources = VERIFY_CODES;
        }

        int codesLen = sources.length();
        Random rand = new Random(System.currentTimeMillis());
        StringBuilder verifyCode = new StringBuilder(verifySize);
        for (int i = 0; i < verifySize; i++){
            verifyCode.append(sources.charAt(rand.nextInt(codesLen -1 )));
        }
        return verifyCode.toString();
    }

    /**
     * Output Random Verification Code Picture Stream and Return Value of Verification Code
     * @param w
     * @param h
     * @param outputFile
     * @param verifySize
     * @return
     */
    public static String outputVerifyImage(int w, int h, File outputFile, int verifySize) throws  IOException{
        String verifyCode = generateVerifyCode(verifySize);
        outputImage(w,h,outputFile,verifyCode);
        return verifyCode;
    }

    /**
     * Generate the specified authentication code image file
     * @param w
     * @param h
     * @param outputFile
     * @param verifyCode
     * @throws IOException
     */
    public static void outputImage(int w, int h, File outputFile, String verifyCode) throws IOException{
        if (outputFile == null){
            return;
        }
        File dir = outputFile.getParentFile();
        if(!dir.exists()){
            dir.mkdirs();
        }

        try {
            outputFile.createNewFile();
            FileOutputStream fos = new FileOutputStream(outputFile);
            outputImage(w, h, fos, verifyCode);
            fos.close();
        } catch (IOException e) {
            throw e;
        }
    }

    public static void outputImage(int w, int h, OutputStream os, String code) throws IOException{
        int verifySize = code.length();
        BufferedImage image = new BufferedImage(w,h, BufferedImage.TYPE_INT_RGB);
        Random rand = new Random();
        Graphics2D g2 = image.createGraphics();
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
        Color[] colors = new Color[5];
        Color[] colorSpaces = new Color[]{Color.WHITE, Color.CYAN, Color.GRAY,
                Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE,
                Color.PINK, Color.YELLOW};
        float[] fractions = new float[colors.length];
        for (int i = 0; i < colors.length; i++){
            colors[i] = colorSpaces[rand.nextInt(colorSpaces.length)];
            fractions[i] = rand.nextFloat();
        }
        Arrays.sort(fractions);

        g2.setColor(Color.GRAY);    //Set border color
        g2.fillRect(0,0, w, h);

        Color c = getRandColor(200, 250);
        g2.setColor(c); //Setting Background Colors
        g2.fillRect(0, 2, w, h-4);

        //Drawing interference lines
        Random random = new Random();
        g2.setColor(getRandColor(160, 200));// Set the color of the line
        for (int i = 0; i < 20; i++) {
            int x = random.nextInt(w - 1);
            int y = random.nextInt(h - 1);
            int xl = random.nextInt(6) + 1;
            int yl = random.nextInt(12) + 1;
            g2.drawLine(x, y, x + xl + 40, y + yl + 20);
        }

        // add noise
        float yawpRate = 0.05f;// Noise Rate
        int area = (int) (yawpRate * w * h);
        for (int i = 0; i < area; i++) {
            int x = random.nextInt(w);
            int y = random.nextInt(h);
            int rgb = getRandomIntColor();
            image.setRGB(x, y, rgb);
        }

        shear(g2, w, h, c);// Distort the picture

        g2.setColor(getRandColor(100, 160));
        int fontSize = h-4;
        Font font = new Font("Algerian", Font.ITALIC, fontSize);
        g2.setFont(font);
        char[] chars = code.toCharArray();
        for(int i = 0; i < verifySize; i++){
            AffineTransform affine = new AffineTransform();
            affine.setToRotation(Math.PI / 4 * rand.nextDouble() * (rand.nextBoolean() ? 1 : -1), (w / verifySize) * i + fontSize/2, h/2);
            g2.setTransform(affine);
            g2.drawChars(chars, i, 1, ((w-10) / verifySize) * i + 5, h/2 + fontSize/2 - 10);
        }

        g2.dispose();
        ImageIO.write(image, "jpg", os);

    }

    private static Color getRandColor(int fc, int bc) {
        if (fc > 255)
            fc = 255;
        if (bc > 255)
            bc = 255;
        int r = fc + random.nextInt(bc - fc);
        int g = fc + random.nextInt(bc - fc);
        int b = fc + random.nextInt(bc - fc);
        return new Color(r, g, b);
    }

    private static int getRandomIntColor() {
        int[] rgb = getRandomRgb();
        int color = 0;
        for (int c : rgb) {
            color = color << 8;
            color = color | c;
        }
        return color;
    }

    private static int[] getRandomRgb() {
        int[] rgb = new int[3];
        for (int i = 0; i < 3; i++) {
            rgb[i] = random.nextInt(255);
        }
        return rgb;
    }

    private static void shear(Graphics g, int w1, int h1, Color color) {
        shearX(g, w1, h1, color);
        shearY(g, w1, h1, color);
    }

    private static void shearX(Graphics g, int w1, int h1, Color color) {

        int period = random.nextInt(2);

        boolean borderGap = true;
        int frames = 1;
        int phase = random.nextInt(2);

        for (int i = 0; i < h1; i++) {
            double d = (double) (period >> 1)
                    * Math.sin((double) i / (double) period
                    + (6.2831853071795862D * (double) phase)
                    / (double) frames);
            g.copyArea(0, i, w1, 1, (int) d, 0);
            if (borderGap) {
                g.setColor(color);
                g.drawLine((int) d, i, 0, i);
                g.drawLine((int) d + w1, i, w1, i);
            }
        }

    }

    private static void shearY(Graphics g, int w1, int h1, Color color) {

        int period = random.nextInt(40) + 10; // 50;

        boolean borderGap = true;
        int frames = 20;
        int phase = 7;
        for (int i = 0; i < w1; i++) {
            double d = (double) (period >> 1)
                    * Math.sin((double) i / (double) period
                    + (6.2831853071795862D * (double) phase)
                    / (double) frames);
            g.copyArea(i, 0, 1, h1, 0, (int) d);
            if (borderGap) {
                g.setColor(color);
                g.drawLine(i, (int) d, i, 0);
                g.drawLine(i, (int) d + h1, i, h1);
            }
        }
    }

public static void main(String[] args) {
        File dir = new File("/Users/zcqshine/Downloads/test");
        int w = 200, h= 80;
        for (int i = 0; i< 50; i++){
            String verifyCode = generateVerifyCode(4);
            File file = new File(dir, verifyCode + ".jpg");
            try {
                outputImage(w, h, file, verifyCode);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Posted by lemming_ie on Sat, 29 Jun 2019 10:32:55 -0700