• 参考文章
  • 要求:
    • 生成随机四位验证码
    • 生成尺寸合适、四方形的图片
    • 打印上验证码,画干扰线和脏点(噪点),图片扭曲
    • 返回图片给controller,输出到客户端
  • util层生成图片型随机验证码

    /**
     * @Author haien
     * @Description 生成图片型随机验证码
     * @Date 2019/2/9
     **/
    public class RandomValidateCodeUtil {
        //随机验证码原材料
        private static String randString="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
        //图片宽
        private static int width=95;
        //高
        private static int height=40;
        //干扰线数量
        private static int interLine=6;
        //随机数生成器(整个类共用,避免数据相同)
        private static Random random = new Random();
    
        /**
         * @Author haien
         * @Description 绘制图片验证码,返回BufferedImage对象
         * @Date 2019/2/9
         * @Param [backColor]
         * @return java.awt.image.BufferedImage
         **/
        public static BufferedImage getImageFromCode(String randCode){
            //创建不透明BufferedImage对象
            BufferedImage image=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
            //获取画布
            Graphics g=image.getGraphics();
            Random random=new Random();
            //画着色矩形
            g.setColor(Color.WHITE);
            g.fillRect(0,0,width,height);
            //绘制干扰线
            int x= random.nextInt(4),y=0;
            int x1=width-random.nextInt(4),y1=0;
            for(int i=0;i<interLine;i++){
                g.setColor(getRandomColor(100,200)); //比较浅色
                y=random.nextInt(height- random.nextInt(4));
                y1= random.nextInt(height-random.nextInt(4));
                g.drawLine(x,y,x1,y1);
            }
            //写上验证码
            int fsize=(int)(height*0.8); //字体大小为图片高度的80%
            int fx=10,fy=fsize;
            g.setFont(new Font(Font.SANS_SERIF, Font.PLAIN,fsize));
            for(int i=0;i<randCode.length();i++){ //一个一个字符写
                fy=(int)(Math.random()*0.8+0.8*height); //字符高低随机
                g.setColor(getRandomColor(40,90)); //比较深色
                g.drawString(randCode.charAt(i)+" ",fx,fy);
                fx+=(width/randCode.length())*(Math.random()*0.3+0.8); //依据宽度浮动
            }
            //扭曲图片
            shearX(g,width,height);
            shearY(g,width,height);
            //添加噪点
            float yawpRate = 0.05f;// 噪声率
            int area = (int) (yawpRate * width * height);//噪点数量
            for (int i = 0; i < area; i++) {
                int xxx = random.nextInt(width);
                int yyy = random.nextInt(height);
                int rgb = random.nextInt(interLine);
                image.setRGB(xxx, yyy, rgb);
            }
            //封笔
            g.dispose();
            return image;
        }
    
        /**
         * @Author haien
         * @Description 获取四位随机验证码
         * @Date 2019/2/9
         * @Param [random]
         * @return java.lang.String
         **/
        public static String getRandomCode(){
            Random random=new Random();
            StringBuilder stringBuilder=new StringBuilder();
            for(int i=0;i<4;i++)
                stringBuilder.append(String.valueOf(randString.charAt(
                                        random.nextInt(randString.length()))));
            return stringBuilder.toString();
        }
    
        /**
         * @Author haien
         * @Description 根据自费颜色和背景颜色计算随机颜色
         * @Date 2019/2/9
         * @Param [fc, bc]
         * @return java.awt.Color
         **/
        private static Color getRandomColor(int fc,int bc){
            if (fc > 255){
                fc = 255;
            }
            if (bc > 255){
                bc = 255;
            }
    
            int r = fc + random.nextInt(bc - fc - 16);
            int g = fc + random.nextInt(bc - fc - 14);
            int b = fc + random.nextInt(bc - fc - 18);
            return new Color(r, g, b);
        }
    
        /**
         * @Author haien
         * @Description 扭曲图片
         * @Date 2019/2/9
         * @Param [g, w, h, color]
         * @return void
         **/
        private static void shearX(Graphics g,int w,int h){ //水平平移
            Random random=new Random();
            int period=5;
            boolean borderGap=true;
            int frames=1;
            int phase=random.nextInt(2);
    
            //一小块一小块平移实现扭曲
            for(int i=0;i<h;i++){
                double dx=(double) (period >> 1)* Math.sin((double) i / (double) period
                        + (2.2831853071795862D * (double) phase)/ (double) frames);
                g.copyArea(0,i,w,1,(int)dx,0);
                if(borderGap){
                    g.setColor(getRandomColor(90,156));
                    g.drawLine((int)dx,i,0,i);
                    g.drawLine((int)dx+w,i,w,i);
                }
            }
        }
        private static void shearY(Graphics g, int w1, int h1) {
            int period = 10;
            boolean borderGap = true;
            int frames = 20;
            int phase = random.nextInt(2);
    
            for (int i = 0; i < w1; i++) {
                double dy = (double) (period >> 1)
                        * Math.sin((double) i / (double) period
                        + (2.2831853071795862D * (double) phase) / (double) frames);
                g.copyArea(i, 0, 1, h1, 0, (int) dy);
                if (borderGap) {
                    g.setColor(getRandomColor(80,99));
                    g.drawLine(i, (int) dy, i, 0);
                    g.drawLine(i, (int) dy + h1, i, h1);
                }
            }
        }
    }
    
  • 图片效果
  • controller层获取图片

    /**
     * @Author haien
     * @Description 获取图片验证码
     * @Date 2019/2/9
     * @Param [request, response]
     * @return void
     **/
    @RequestMapping("/getVerifyCode")
    public void getVerifyCode(HttpServletRequest request,HttpServletResponse response) throws IOException {
        response.setContentType("image/jpeg");
        response.setHeader("Pragma","No-cache");
        response.setHeader("Cache-Control","no-cache");
        response.setDateHeader("Expire",0);
    
        String randCode=RandomValidateCodeUtil.getRandomCode(); //获取4位验证码
        HttpSession session=request.getSession();
        session.removeAttribute("RANDOMVALIDATECODEKEY");
        session.setAttribute("RANDOMVALIDATECODEKEY",randCode);
        BufferedImage image=RandomValidateCodeUtil.getImageFromCode(randCode); //生成图片
        logger.info("----获取图片验证码--RANDOMVALIDATECODEKEY={}----",randCode);
        try {
            ImageIO.write(image,"JPEG",response.getOutputStream());
        } catch (IOException e) {
            throw new IOException("获取客户端输出流失败");
        }
    }
    
  • test测试:将图片保存到本地

    public static void main(String[] args) {
        String randomCode = RandomValidateCodeUtil.getRandomCode();
        System.out.println(randomCode);
        BufferedImage imageFromCode = RandomValidateCodeUtil.getImageFromCode(randomCode);
        try {
            Path path=Paths.get("d:/test01.jpg");
            if(Files.exists(path)) Files.delete(path);
            File file = path.toFile();
            ImageIO.write(imageFromCode,"jpg",file);
            System.out.println("成功保存到:"+file.getAbsolutePath());
        } catch (IOException e) {
            System.out.println("保存失败");
            e.printStackTrace();
        }
    }
    
  • 代码实例:github/image_verifyCode分支/thz-parent/thz-manager-web/WebPageController、thz-common/tool/RandomValidateCodeUtil、RandomValidateCodeUtilTest