云迈博客

您现在的位置是:首页 > 后端开发 > Java > 正文

Java

Java 生成滑动图片验证码, 阴影, 切块

凌玄龙2021-05-07Java563
1,效果2,切图工具importjavax.imageio.ImageIO;importjavax.imageio.ImageReadParam;importjavax.image

1, 效果
2, 切图工具

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Iterator;

public class ImageUtil {

    public static void cut(int x,int y,int width,int height,String srcpath,String subpath) throws IOException {//裁剪方法
        FileInputStream is=null;
        ImageInputStream iis=null;
        try{
            is=new FileInputStream(srcpath); //读取原始图片
            Iterator<ImageReader>it=ImageIO.getImageReadersByFormatName("jpg"); //ImageReader声称能够解码指定格式
            ImageReader reader=it.next();
            iis=ImageIO.createImageInputStream(is); //获取图片流
            reader.setInput(iis, true); //将iis标记为true(只向前搜索)意味着包含在输入源中的图像将只按顺序读取
            ImageReadParam param=reader.getDefaultReadParam(); //指定如何在输入时从 Java Image I/O框架的上下文中的流转换一幅图像或一组图像
            Rectangle rect=new Rectangle(x, y, width, height); //定义空间中的一个区域
            param.setSourceRegion(rect); //提供一个 BufferedImage,将其用作解码像素数据的目标。
            BufferedImage bi=reader.read(0, param); //读取索引imageIndex指定的对象
            ImageIO.write(bi, "jpg", new File(subpath)); //保存新图片
        }finally{
            if(is!= null)
                is.close();
            if(iis != null)
                iis.close();
        }
    }

    public void cutByTemplate2(BufferedImage oriImage,BufferedImage newSrc,BufferedImage newSrc2,int x,int y,int width,int height, int c_a, int c2_b){
        //固定圆半径为5
        int c_r=10;
        double rr=Math.pow(c_r, 2);//r平方
        //圆心的位置 cb
        //System.out.println(c_a);
        int c_b=y;

        //第二个圆(排除圆内的点) c2_a
        int c2_a=x;

        //System.out.println(oriImage.getWidth()+"   "+oriImage.getHeight());
        for(int i=0;i<oriImage.getWidth();i++){
            for(int j=0;j<oriImage.getHeight();j++){
                //data[i][j]=oriImage.getRGB(i,j);

                //(x-a)²+(y-b)²=r²中,有三个参数a、b、r,即圆心坐标为(a,b),半径r。
                double f=Math.pow((i-c_a), 2)+Math.pow((j-c_b), 2);

                double f2=Math.pow((i-c2_a), 2)+Math.pow((j-c2_b), 2);

                int rgb=oriImage.getRGB(i,j);
                if(i>=x&&i<(x+width) &&j>=y&&j<(y+height) && f2>=rr){//在矩形内
                    //块范围内的值
                    in(newSrc, newSrc2, i, j, rgb);
                }else if(f<=rr){
                    //在圆内
                    in(newSrc, newSrc2, i, j, rgb);
                }else{
                    //剩余位置设置成透明
                    out(newSrc, newSrc2, i, j, rgb);
                }

            }
        }
    }

    private void in(BufferedImage newSrc,BufferedImage newSrc2,int i,int j,int rgb){
        newSrc.setRGB(i, j, rgb);
        //原图设置变灰
        int r = (0xff & rgb);
        int g = (0xff & (rgb >> 8));
        int b = (0xff & (rgb >> 16));
        rgb = r + (g << 8) + (b << 16) + (100 << 24);
        //rgb = r + (g << 8) + (b << 16);
        newSrc2.setRGB(i, j, rgb);
        newSrc.setRGB(i + 1, j + 1, rgb);
    }

    private void out(BufferedImage newSrc,BufferedImage newSrc2,int i,int j,int rgb){
        newSrc.setRGB(i, j, 0x00ffffff);
        newSrc2.setRGB(i, j, rgb);
    }


    public static BufferedImage createDropShadow(BufferedImage image,
                                                 int size, float opacity) {
        int width = image.getWidth() + size * 2;
        int height = image.getHeight() + size * 2;
        BufferedImage mask = new BufferedImage(width, height,
                BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = mask.createGraphics();
        g2.drawImage(image, size, size, null);
        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN,
                opacity));
        g2.setColor(Color.BLACK);
        g2.fillRect(0, 0, width, height);
        g2.dispose();
        BufferedImage shadow = createBlurOp(size).filter(mask, null);
        g2 = shadow.createGraphics();
        g2.dispose();
        return shadow;
    }
    private static ConvolveOp createBlurOp(int size) {
        float[] data = new float[size * size];
        float value = 1f / (float) (size * size);
        for (int i = 0; i < data.length; i++) {
            data[i] = value;
        }
        return new ConvolveOp(new Kernel(size, size, data),
                ConvolveOp.EDGE_NO_OP, null);
    }
}

3, 生成背景图
x就是验证码, x 轴的偏移量, 验证就是需要这个

@GetMapping("/xcode")
    public Result getXCode1(HttpServletResponse resp, HttpServletRequest request) throws IOException {
        ImageUtil tt = new ImageUtil();

        // 随机选择背景图
        int num = new Random().nextInt(10) + 1;
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("xcode/" + num + ".jpg");
        BufferedImage src= ImageIO.read(resourceAsStream);
        //移动图
        BufferedImage newSrc=new BufferedImage(src.getWidth(), src.getHeight(),BufferedImage.TYPE_4BYTE_ABGR);//新建一个类型支持透明的BufferedImage
        //对比图
        BufferedImage newSrc2=new BufferedImage(src.getWidth(), src.getHeight(),BufferedImage.TYPE_4BYTE_ABGR);//新建一个类型支持透明的BufferedImage

        //抠块的大小
        int blockWidth=48;
        int blockHeight=48;

        // 用于生成 移动图
        XCodeTdo code = new XCodeTdo();
        code.setNum(num);

        Random rand1=new Random();
        int x=rand1.nextInt(src.getWidth()-blockWidth-20)+20;//10,width-200
        if (x > 210 - 58) {
            x = 210 - 58;
        }

        Random rand2=new Random();
        int y=rand2.nextInt(src.getHeight()-blockHeight-20)+20;//

        code.setX(x);
        code.setY(y);

        int ca = new Random().nextInt(blockWidth-2*20)+(x+20);
        int cb = new Random().nextInt(blockHeight-2*20)+(y+20);
        code.setCa(ca);
        code.setCb(cb);

        tt.cutByTemplate2(src,newSrc,newSrc2,x,y,blockWidth,blockHeight, ca, cb);//图片大小是固定,位置是随机

        request.getSession().setAttribute("X_CODE", JSON.toJSONString(code));

        //生成对比图
        ImageIO.write(newSrc2, "png", resp.getOutputStream());
        return null;
    }

4, 生成区块图

@GetMapping("/xcodem")
    public Result getXCode2(HttpServletResponse resp, HttpServletRequest request) throws IOException {
        ImageUtil tt = new ImageUtil();

        Object o = request.getSession().getAttribute("X_CODE");
        if (o == null) {
            return new Result();
        }

        XCodeTdo codeTdo = JSON.parseObject(String.valueOf(o), XCodeTdo.class);

        int num = codeTdo.getNum();
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("xcode/" + num + ".jpg");
        BufferedImage src= ImageIO.read(resourceAsStream);
        //移动图
        BufferedImage newSrc=new BufferedImage(src.getWidth(), src.getHeight(),BufferedImage.TYPE_4BYTE_ABGR);//新建一个类型支持透明的BufferedImage
        //对比图
        BufferedImage newSrc2=new BufferedImage(src.getWidth(), src.getHeight(),BufferedImage.TYPE_4BYTE_ABGR);//新建一个类型支持透明的BufferedImage

        //抠块的大小
        int x= codeTdo.getX();
        int y= codeTdo.getY();
        tt.cutByTemplate2(src,newSrc,newSrc2,x,y,48,48, codeTdo.getCa(), codeTdo.getCb());//图片大小是固定,位置是随机

        newSrc = newSrc.getSubimage(x - 1, 0, 58, 210);

        //ImageUtil.createDropShadow(newSrc, 3, 0.08f);
        //生成对比图
        ImageIO.write(newSrc, "png", resp.getOutputStream());
        return null;
    }

5, 阴影图

@GetMapping("/xcodey")
    public void getXCode3(HttpServletResponse resp, HttpServletRequest request) throws IOException {
        ImageUtil tt = new ImageUtil();


        Object o = request.getSession().getAttribute("X_CODE");
        if (o == null) {
            return;
        }

        XCodeTdo codeTdo = JSON.parseObject(String.valueOf(o), XCodeTdo.class);

        int num = codeTdo.getNum();
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("xcode/" + num + ".jpg");
        BufferedImage src= ImageIO.read(resourceAsStream);
        //移动图
        BufferedImage newSrc=new BufferedImage(src.getWidth(), src.getHeight(),BufferedImage.TYPE_4BYTE_ABGR);//新建一个类型支持透明的BufferedImage
        //对比图
        BufferedImage newSrc2=new BufferedImage(src.getWidth(), src.getHeight(),BufferedImage.TYPE_4BYTE_ABGR);//新建一个类型支持透明的BufferedImage

        //抠块的大小
        int x= codeTdo.getX();
        int y= codeTdo.getY();
        tt.cutByTemplate2(src,newSrc,newSrc2,x,y,48,48, codeTdo.getCa(), codeTdo.getCb());//图片大小是固定,位置是随机
        newSrc = newSrc.getSubimage(x - 1, 0, 48, 210);
        newSrc = ImageUtil.createDropShadow(newSrc, 4, 0.6f);

        //生成对比图
        ImageIO.write(newSrc, "png", resp.getOutputStream());
    }
public class XCodeTdo {
    private int num;
    private int x;
    private int y;
    private int ca;
    private int cb;

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public int getCa() {
        return ca;
    }

    public void setCa(int ca) {
        this.ca = ca;
    }

    public int getCb() {
        return cb;
    }

    public void setCb(int cb) {
        this.cb = cb;
    }
}

4, 怎么验证?
前端需要传 x 轴的偏移量, 与第一次生成的背景图保存在session 中的 X_CODE, 中的x进行比较值

@RequestMapping(value = "/login", method = RequestMethod.POST)
    @ResponseBody
    public Object loginVali(HttpServletRequest request) {

        String username = super.getPara("username").trim();
        String password = super.getPara("password").trim();
        String xcode= super.getPara("xcode");

        int token = Integer.parseInt(xcode);
        Object obj = request.getSession().getAttribute("X_CODE");
        XCodeTdo codeTdo = JSON.parseObject(String.valueOf(obj), XCodeTdo.class);
        // 这里 / 2.1 是因为前端的滑动器值是100
        int x = (int) (codeTdo.getX() / 2.1);
        // 预留了正负6 * 2.1 = 13的像素的偏差
        if (token > x + 3 || token < x - 3) {
            // 删除验证记录
            request.getSession().removeAttribute("X_CODE");
            return new SuccessTip(400,"验证码错误!",null);
        }

        return new SuccessTip(200,"登录成功!",null);
    }

发表评论

评论列表

  • 这篇文章还没有收到评论,赶紧来抢沙发吧~