All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fastchar.captcha.out.FastOutCaptchaOfClick Maven / Gradle / Ivy

package com.fastchar.captcha.out;

import com.fastchar.captcha.FastCaptchaConfig;
import com.fastchar.core.FastAction;
import com.fastchar.core.FastChar;
import com.fastchar.out.FastOut;
import com.fastchar.servlet.http.FastHttpServletResponse;
import com.fastchar.utils.FastMD5Utils;
import com.fastchar.utils.FastNumberUtils;
import com.fastchar.utils.FastStringUtils;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.font.TextAttribute;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.List;
import java.util.*;

/**
 * 看图依次点击文字位置
 */
public class FastOutCaptchaOfClick extends FastOut {

    private static final String DEFAULT_CAPTCHA_KEY = "FastChar-Captcha";

    private static final Color[] SHADOW_COLOR = new Color[]{
            Color.BLACK,
            Color.RED,
            Color.BLUE,
            Color.YELLOW,
            Color.GREEN,
            Color.MAGENTA
    };
    private static final Set RANDOM_BACKGROUND_INDEX = new HashSet<>();

    public FastOutCaptchaOfClick() {
        this.contentType = "image/jpeg";
    }

    @Override
    public void response(FastAction action) throws Exception {
        FastHttpServletResponse response = action.getResponse();
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        response.setStatus(getStatus());
        response.setContentType(toContentType(action));

        List keys = new ArrayList<>(5);
        for (int i = 0; i < 5; i++) {
            keys.add(String.valueOf(randomChar()));
        }
        outCaptcha(action, keys);
    }


    public boolean validateCaptcha(FastAction action, List points) {
        String captchaKey = action.getParam("captchaKey", DEFAULT_CAPTCHA_KEY);
        List captcha = action.getSession(captchaKey);
        if (captcha == null) {
            return false;
        }
        if (captcha.isEmpty()) {
            return false;
        }
        if (points.size() > captcha.size()) {
            return false;
        }
        for (int i = 0; i < points.size(); i++) {
            String pointStr = points.get(i);
            String[] pointArray = pointStr.split(",");
            int x = FastNumberUtils.formatToInt(pointArray[0]);
            int y = FastNumberUtils.formatToInt(pointArray[1]);
            Rectangle rectangle = captcha.get(i);
            if (!rectangle.contains(x, y)) {
                return false;
            }
        }
        String value = FastMD5Utils.MD5(FastStringUtils.buildUUID());
        action.setSession(captchaKey, value);
        action.setSession("validateCode", value);
        return true;
    }


    public List getContents(FastAction action) {
        String captchaKey = action.getParam("captchaKey", DEFAULT_CAPTCHA_KEY);
        return action.getSession(captchaKey + "Content");
    }

    public void resetCaptcha(FastAction action) {
        String captchaKey = action.getParam("captchaKey", DEFAULT_CAPTCHA_KEY);
        action.removeSession(captchaKey);
    }


    private void outCaptcha(FastAction action, List contents) throws Exception {
        String captchaKey = action.getParam("captchaKey", DEFAULT_CAPTCHA_KEY);

        FastCaptchaConfig config = FastChar.getConfig(FastCaptchaConfig.class);

        List captchaFonts = config.getCaptchaFonts();
        List captchaBackgroundImages = config.getCaptchaBackgroundImages();
        if (RANDOM_BACKGROUND_INDEX.size() >= captchaBackgroundImages.size()) {
            RANDOM_BACKGROUND_INDEX.clear();
        }

        int backgroundIndex;
        do {
            backgroundIndex = new Random().nextInt(captchaBackgroundImages.size());
        } while (RANDOM_BACKGROUND_INDEX.contains(backgroundIndex));

        RANDOM_BACKGROUND_INDEX.add(backgroundIndex);

        URL backgroundImg = captchaBackgroundImages.get(backgroundIndex);
        Map stringFont = new HashMap<>(contents.size());
        for (String string : contents) {
            if (config.isSimple()) {
                stringFont.put(string, config.getSimpleFontURL());
            }else{
                stringFont.put(string, captchaFonts.get(new Random().nextInt(captchaFonts.size())));
            }
        }

        Image backgroundImgIO = ImageIO.read(backgroundImg);

        int imgIOWidth = backgroundImgIO.getWidth(null);
        int imgIOHeight = backgroundImgIO.getHeight(null);

        BufferedImage bufferedImage = new BufferedImage(imgIOWidth, imgIOHeight, BufferedImage.TYPE_INT_RGB);

        Graphics2D graphics2D = bufferedImage.createGraphics();
        antialiasing(graphics2D);
        graphics2D.drawImage(backgroundImgIO.getScaledInstance(imgIOWidth, imgIOHeight, Image.SCALE_SMOOTH), 0, 0, null);


        int directionType = new Random().nextInt(2);

        int index = 0;
        float margin = Math.min(imgIOHeight, imgIOWidth) * 0.2f;
        float avgHSpace = imgIOWidth / (float) stringFont.size();
        float avgVSpace = imgIOHeight / (float) stringFont.size();
        float fontSize = Math.min(imgIOHeight, imgIOWidth) / (contents.size() * 1.5f);

        int shadowSize = 5;//投影的大小
        int span = 5;//允许超出边界距离


        Map stringRectangle = new HashMap<>(contents.size());
        for (Map.Entry stringURLEntry : stringFont.entrySet()) {
            String content = stringURLEntry.getKey();
            Font font = getFont(fontSize, stringURLEntry.getValue());
            graphics2D.setFont(font);
            
            FontMetrics fontMetrics = graphics2D.getFontMetrics();
            Rectangle2D contentBound = fontMetrics.getStringBounds(content, graphics2D);

            float y;
            float x;
            if (directionType == 0) {//横向
                x = index * avgHSpace;
                y = (float) FastNumberUtils.random(fontMetrics.getAscent(), imgIOHeight - contentBound.getHeight()) + fontMetrics.getAscent();
            } else {
                y = index * avgVSpace + fontMetrics.getAscent();
                x = (float) FastNumberUtils.random(0, imgIOWidth - contentBound.getWidth());
            }

            x = Math.max(x, margin);
            y = Math.max(y, margin);

            if (config.isSimple()) {
                graphics2D.setColor(Color.WHITE);
            }else{
                graphics2D.setColor(SHADOW_COLOR[new Random().nextInt(SHADOW_COLOR.length)]);
            }
            graphics2D.drawString(content, x + shadowSize, y + shadowSize);

            Color color1 = randomColor();
            Color color2 = randomColor();

            if (config.isSimple()) {
                graphics2D.setColor(Color.BLACK);
            }else{
                GradientPaint paint = new GradientPaint(20, 20, color1, (float) (contentBound.getWidth() * 0.6), (float) (contentBound.getHeight() * 0.7), color2, true);
                graphics2D.setPaint(paint);// 设置渐变
            }
            graphics2D.drawString(content, x, y);

            stringRectangle.put(content, new Rectangle((int) x - span, (int) y - fontMetrics.getAscent() - span, (int) contentBound.getWidth() + shadowSize + span, (int) contentBound.getHeight() + shadowSize + span));
            index++;
        }

        Collections.shuffle(contents);
        List responseContent = new ArrayList<>(contents.size());
        for (String key : contents) {
            responseContent.add(stringRectangle.get(key));
        }

        action.setSession(captchaKey, responseContent);
        action.setSession(captchaKey + "Content", contents);

        try (OutputStream outputStream = action.getResponse().getOutputStream()) {
            ImageIO.write(bufferedImage, "jpg", outputStream);
            outputStream.flush();
        }
    }


    private void antialiasing(Graphics2D g2d) {
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    }

    private Font getFont(double fontSize, URL fontURL) throws IOException, FontFormatException {

        Map attrs = new HashMap<>();
        attrs.put(TextAttribute.SIZE, fontSize);
        attrs.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);

        if (new Random().nextInt(10) % 2 == 0) {
            attrs.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
        }

        try (InputStream fontStream = fontURL.openStream()) {
            Font font = Font.createFont(Font.TRUETYPE_FONT, fontStream);
            return font.deriveFont(attrs);
        }
    }

    private char randomChar() {
        String str = "";
        int heightPos;
        int lowPos;

        Random random = new Random();

        heightPos = (176 + Math.abs(random.nextInt(39)));
        lowPos = (161 + Math.abs(random.nextInt(93)));

        byte[] b = new byte[2];
        b[0] = (Integer.valueOf(heightPos)).byteValue();
        b[1] = (Integer.valueOf(lowPos)).byteValue();

        try {
            str = new String(b, "GBK");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return str.charAt(0);
    }

    private Color randomColor() {
        Random random = new Random();
        int r = random.nextInt(255);
        int g = random.nextInt(255);
        int b = random.nextInt(255);
        return new Color(r, g, b, 255);
    }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy