验证码功能实现

最近公司对公司小程序进行安全漏洞检测,发现我们的登录接口存在安全隐患,需要添加图形验证码来进行过滤,要实现图形验证码要考虑两个问题

1,图片展现的形式

 1)二进制传到前端直接展示

 2)存到一个图片地址,返回url前端直接获取

2,如何定位 

后端生成一个key,验证码,存到缓存中,传给前端,前端传验证码,key到后端

啥也不说了,直接上代码

ImageVerificationCode.java

package org.source.dsmh.utils;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Random;

import javax.imageio.ImageIO;

import org.apache.commons.lang3.ArrayUtils;

import sun.misc.BASE64Encoder;


public class ImageVerificationCode {

     private  volatile  static  ImageVerificationCode imageCode;

        private  ImageVerificationCode(){}

        public  static  ImageVerificationCode getInstance(){
            if(imageCode==null){
                synchronized (ImageVerificationCode.class){
                    if(imageCode==null){
                        imageCode=new ImageVerificationCode();
                    }
                }
            }
            return  imageCode;
        }
    private static Random r = new Random();    //获取随机数对象
    //private String[] fontNames = {"宋体", "华文楷体", "黑体", "微软雅黑", "楷体_GB2312"};   //字体数组
 
    //验证码数组
   // private static String codes = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    private static String codes = "1234567890";

    /**
     * 获取随机的颜色
     *
     * @return
     */
    private Color randomColor() {
        int r = this.r.nextInt(225);  //这里为什么是225,因为当r,g,b都为255时,即为白色,为了好辨认,需要颜色深一点。
        int g = this.r.nextInt(225);
        int b = this.r.nextInt(225);
        return new Color(r, g, b);            //返回一个随机颜色
    }

  

    /**
     * 获取随机字符串
     *
     * @return
     */
    public  String randomStr() {
        StringBuilder sb=new StringBuilder();
        for(int i=0;i<4;i++) {
            int index = r.nextInt(codes.length());
            char x=codes.charAt(index);
            sb.append(x);
        }         
        return sb.toString();
    }


    
    public  String createImageWithVerifyCode(int width, int height, String word) throws IOException {
        String png_base64="";
        //绘制内存中的图片
        BufferedImage bufferedImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
        //得到画图对象
        Graphics graphics = bufferedImage.getGraphics();
        //绘制图片前指定一个颜色
        graphics.setColor( Color.white);
        graphics.fillRect(0,0,width,height);
        //绘制边框
        graphics.setColor(Color.white);
        graphics.drawRect(0, 0, width - 1, height - 1);
        // 步骤四 四个随机数字
        Graphics2D graphics2d = (Graphics2D) graphics;
        graphics2d.setFont(new Font("宋体", Font.BOLD, 18));
        Random random = new Random();
                
        // 定义x坐标
        int x = 5;
        for (int i = 0; i < word.length(); i++) {
            // 随机颜色
            //graphics2d.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));
            graphics2d.setColor(new Color(0,29,38));
            // 旋转 -30 --- 30度
            int jiaodu = random.nextInt(60) - 30;
            // 换算弧度
            double theta = jiaodu * Math.PI / 180;
            // 获得字母数字
            char c = word.charAt(i);
            //将c 输出到图片
            graphics2d.rotate(theta, x, 20);
            graphics2d.drawString(String.valueOf(c), x, 20);
            graphics2d.rotate(-theta, x, 20);
            x += 18;
        }
        //保存验证码
        
        // 绘制干扰线
        graphics.setColor(this.randomColor());
        int x1;
        int x2;
        int y1;
        int y2;
        for (int i = 0; i < 30; i++) {
            x1 = random.nextInt(width);
            x2 = random.nextInt(12);
            y1 = random.nextInt(height);
            y2 = random.nextInt(12);
           // graphics.drawLine(x1, y1, x1 + x2, x2 + y2);
        }
        graphics.dispose();// 释放资源
        ByteArrayOutputStream baos = new ByteArrayOutputStream();//io流
        ImageIO.write(bufferedImage, "png", baos);//写入流中
        byte[] bytes = baos.toByteArray();//转换成字节
        BASE64Encoder encoder = new BASE64Encoder();
        png_base64 = encoder.encodeBuffer(bytes).trim();
        png_base64= "data:image/jpeg;base64,"+png_base64;
       // png_base64 = png_base64.replaceAll("
", "").replaceAll("
", "");//删除 

        return png_base64;
    }

    
    public static String getRandomString(int length){
       
        Random random=new Random();
        StringBuffer sb=new StringBuffer();
        for(int i=0;i<length;i++){
          int number=random.nextInt(100);
          sb.append(number);
        }
        return sb.toString();
    }
    public static void main(String[] args) throws IOException {
        
        ImageVerificationCode ivc = new ImageVerificationCode();     //用我们的验证码类,生成验证码类对象
        /*
         * String str=ivc.randomStr(); System.out.println(str);
         * System.out.println(ivc.createImageWithVerifyCode(75, 31,str ));
         */
        
        System.out.println(ivc.getRandomString(5));
    }
}

LocalAccountPicCodeServiceImpl.java

package org.source.dsmh.service.impl;

import java.util.concurrent.locks.ReentrantLock;

import org.apache.log4j.Logger;
import org.source.dsmh.service.LocalDataService;
import org.source.dsmh.utils.AccountConstant;
import org.source.dsmh.utils.DataTemplate;
import org.source.dsmh.utils.ImageVerificationCode;
import org.source.dsmh.utils.mongodb.LogMsg;
import org.source.dsmh.utils.redis.RedisOperator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.JSONObject;

/****
 * 图片验证码*/
@Service("local-account-picCode")
public class LocalAccountPicCodeServiceImpl implements LocalDataService {

    private Logger log = Logger.getLogger(LocalAccountPicCodeServiceImpl.class);

    @Autowired
    private RedisOperator redisOperator;

    public static final String PICCODE_PREFIX = "account:code";
    
    
    
    private final ReentrantLock lock=new ReentrantLock();

    @Override
    public DataTemplate localData(String paramJson, String function, String method, String appUser) {

        this.log.info(LogMsg.getLogMsgForInfo(function, method, paramJson, method));         

        /**
         * @info 完成参数转换
         */
        JSONObject jsonObject = null;
        if (!StringUtils.isEmpty(paramJson)) {
            try {
                jsonObject = JSONObject.parseObject(paramJson);
            } catch (Exception e) {
                e.printStackTrace();
                log.error(LogMsg.getLogMsgForError(function, method, paramJson, "", AccountConstant.PARAM_JSON_ERROR),
                        e);
                return DataTemplate.error(AccountConstant.PARAM_JSON_ERROR);
            }
        } else {
            jsonObject = new JSONObject();
        }
      /**
         * @info 随机生成验证码--将验证码发送至用户手机
         */
        
         lock.lock();
        try {
                long time=System.currentTimeMillis();
                String key=time+ImageVerificationCode.getInstance().getRandomString(5);
                final int width = 75; // 图片宽度
                final int height = 31; // 图片高度

                String words = ImageVerificationCode.getInstance().randomStr();
                // 创建验证码图片并返回图片上的字符串
                String code = ImageVerificationCode.getInstance().createImageWithVerifyCode(width, height, words);
                log.info("验证码内容: " + words);
                this.redisOperator.setCache(PICCODE_PREFIX, "code:" + key, words);
                log.info(LogMsg.getLogMsgForInfo(function, method, PICCODE_PREFIX+":code:"+key+"验证码:"+words, AccountConstant.SUCCESS));    
                JSONObject result = new JSONObject();
                result.put("code", code);                                
                result.put("picCodeKey", key);
                return DataTemplate.ok(result);            
        } catch (Exception e) {
            e.printStackTrace();
            log.error(LogMsg.getLogMsgForError(function, method, paramJson, "", "获取验证码失败"), e);
            return DataTemplate.error("获取验证码失败");
        }finally {
            lock.unlock();
        }

    }
}

 LocalAccountLoginvalidateServiceImpl.java

  String picCode=jsonObject.getString("picCode");
    if (StringUtils.isEmpty(picCode)){
            return DataTemplate.error("验证码不能为空");
       }
      String picCodeKey=jsonObject.getString("picCodeKey");             try {
                                                            
                //获取缓存中验证码值,判断验证码是否正确
                 String valid = this.redisOperator.getCache(PICCODE_PREFIX, "code:" + picCodeKey);
                 
                  if (StringUtils.isEmpty(picCode) || !picCode.equalsIgnoreCase(valid)) {
                        log.error(LogMsg.getLogMsgForError(function, method, paramJson, "缓存验证码:"+valid, AccountConstant.VALID_ERROR_MESSAGE));
                        return DataTemplate.error(AccountConstant.VALID_ERROR_MESSAGE);
                  }                 
                                                    
            } catch (Exception e) {
                log.error(LogMsg.getLogMsgForError(function, method, paramJson, "", AccountConstant.VALID_ERROR_MESSAGE));
                return DataTemplate.error(AccountConstant.VALID_ERROR_MESSAGE);
            }
好记性不如烂笔头
原文地址:https://www.cnblogs.com/codehello/p/12921616.html