实现点选随机汉字验证码

23 篇文章 0 订阅
11 篇文章 0 订阅

点选随机汉字验证码实现的几个关键点

  1. 随机生成汉字
  2. 随机背景图片(只使用一张背景图略显尴尬)
  3. 字的布局
  4. 要允许一定的误差

随机生成汉字

/**
     * 生成随机汉字
     * @return
     */
    private  String getRandomChineseChar(){
        String str;
        int heightPos;//定义高低
        int lowPos;
        heightPos=(176+Math.abs(random.nextInt(39)));//获取最高位
        lowPos=(161+Math.abs(random.nextInt(93)));//获取低位值
        byte[] bytes=new byte[2];
        bytes[0]=new Integer(heightPos).byteValue();
        bytes[1]=new Integer(lowPos).byteValue();
        try {
            str=new String(bytes,"GBK");
        } catch (UnsupportedEncodingException e) {
            // e.printStackTrace();
            str="䵵";//如有异常 我们返回这个字
        }
        return str;
    }

生成随机颜色

 /**
     * 生成随机原色
     * @return
     */
    private  Color getRandomColor(){
        return new Color(random.nextInt(255),random.nextInt(255),random.nextInt(255));
    }

生成汉字随机坐标

在这里我固定了几个字的顺序和位置,如果有需要可以对汉字进行顺序打乱后再定位

 /**
     * 生成个汉字和随机坐标
     * @return
     */
    public  Map<String,int[]> createCode(){
        int x=0;
        int y=0;
        Map<String,int[]> map=new HashMap<>();
        for(int i=0;i<chars;i++){
            String str=getRandomChineseChar();
           while (map.containsKey(str)){
               str=getRandomChineseChar();
           }
            switch (i){
                case 0:
                    x=random.nextInt(30)+40;
                    y=random.nextInt(200)+60;
                    break;
                case 1:
                    x=random.nextInt(40)+120;
                    y=random.nextInt(200)+70;
                    break;
                case 2:
                    x=random.nextInt(30)+200;
                    y=random.nextInt(180)+100;
                    break;
                case 3:
                    x=random.nextInt(30)+80;
                    y=random.nextInt(180)+90;
                    break;
                    default:
            }
            map.put(str,new int[]{x,y});
        }
        return map;
    }

生成验证码图片

 /**
     * 生成验证码图片
     * @param credential
     * @return
     */
    public  BufferedImage createCodeImg(String credential){
        Map<String,int[]> codeMap=this.createCode();
        //test 打印坐标
        String xy="";
       for(String key:codeMap.keySet()){
           xy=xy+codeMap.get(key)[0]+":"+codeMap.get(key)[1]+";";
       }
        Logs.error("坐标:{},\n{}",xy, JSONObject.toJSONString(codeMap));
        redis.set(Constants.MSG_IMG_VERIFY_CODE + credential, codeMap, imgVerifyCodeExpireTime);
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2 = (Graphics2D) image.getGraphics();
        // 读取本地图片,做背景图片
       String imgPath=checkImgPath + (random.nextInt(4) + 1) + ".jpg";
        try {
            //读取背景图片
            g2.drawImage(ImageIO.read(new File(imgPath)),0,footSize,width,height,null);
        } catch (IOException e) {
            Logs.error(e.getMessage(),e);
        }
        g2.setColor(Color.WHITE);//设置颜色
        g2.drawRect(0,0,width-1,height-1);//画边框
        g2.setFont(new Font("宋体",Font.ITALIC,footSize));//设置字体
        StringBuffer keysStr=new StringBuffer();
        for(String key:codeMap.keySet()){
            keysStr.append(key);
            g2.setColor(getRandomColor());
            g2.drawString(key,codeMap.get(key)[0],codeMap.get(key)[1]);
       }
        //画提示语
        g2.setColor(Color.WHITE);
        g2.setFont(new Font("宋体",Font.ITALIC,15));//设置字体
        g2.drawString("请依次点击:"+keysStr,0,15);
       //释放资源
        g2.dispose();
        return image;
    }

输出验证码图片

		response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        response.setContentType("image/jpeg");
        ImageIO.write(clickVerifyUtil.createCodeImg(credential),"JPG",response.getOutputStream());

效果图

博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.

// An highlighted block
var foo = 'bar';

验证核心代码

只粘贴了部分核心代码,一些空值之类的检验需要自己完成

    JSONObject obj=redis.get(Constants.MSG_IMG_VERIFY_CODE + credential,JSONObject.class);
        String[] xyArr=xyStr.split(";");
        /**
         * 取出后直接消除,一个图片验证码只能使用一次
         */
        redis.delete(Constants.MSG_IMG_VERIFY_CODE + credential);
        //TODO 比对验证码的坐标在允许误差范围内是否一致
        Object[] keys=obj.keySet().toArray();
        for(int i=0;i<keys.length;i++){
            String key= (String) keys[i];
            JSONArray xyS=obj.getJSONArray(key);
            String[] xyQ=xyArr[i].split(":");
            int x=xyS.getInteger(0);//图片x坐标
            int y=xyS.getInteger(1);//图片y坐标
            int xq=Integer.valueOf(xyQ[0]);//请求的x坐标
            int yq=Integer.valueOf(xyQ[1]);//请求的y坐标
            if(offset(x,xq)>ERROR_ALLOW || offset(y,yq)>ERROR_ALLOW){
                //验证不通过
                return false;
            }
        }
        return true;

前端demo

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<title>验证码</title>
</head>
<body>
<div style="text-align:center;position:relative;">
    <!--<h2>这是点击的验证码</h2> -->
    <!-- 这是点击的验证码 -->
    <img id="codeT3" src="http://127.0.0.1:8080/click?credential=12345678&flag="+Math.random()"/>
    </br>
    <!-- 
        <input type="button" value="刷新" οnclick="getCodeTree();" /> 
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    <input type="button" value="校验" οnclick="cheakOutTree();" />
     -->
    <select id="codeSelect" style="display: none;"></select>
    <img  src="/api/refresh.png" style="position:absolute;right:0;top:0; width:20px; height: 20px;" οnclick="getCodeTree();" />
</div>


<script type="text/javascript">
//点击次数
var number=0;

//获取验证码3
function getCodeTree() {
    number = 0;
    $(".zhezhao").remove();
    document.getElementById("codeSelect").options.length = 0;
    $("#codeT3").attr("src","http://127.0.0.1:8080/click?credential=12345678&flag="+Math.random());
}

$(function() {
    $("#codeT3").bind("click", function(ev) {
        var oEvent = ev || event;
        //var number = $("#codeSelect option").length;
        number++;
        
        if (number > 4) {
            return;
        }       
        var x = oEvent.pageX;
        var y = oEvent.pageY;
        var img = document.getElementById('codeT3');    //获取图片的原点
        var nodex = getNodePosition(img)[0];//原点x 与原点y
        var nodey = getNodePosition(img)[1];
        
        
        var xserver = parseInt(x) - parseInt(nodex);
        var yserver = parseInt(y) - parseInt(nodey);
    
    
        $("#codeSelect").append(
        "<option value='"+ (parseInt(number)+1) +"'>" + xserver + ":" + yserver
        + "</option>");
        var oDiv = document.createElement('img');
        oDiv.style.left = (parseInt(x)-10) + 'px'; // 指定创建的DIV在文档中距离左侧的位置    图片大小30 左右移动5
        oDiv.style.top = (parseInt(y) -15) + 'px'; // 指定创建的DIV在文档中距离顶部的位置
        oDiv.style.border = '1px solid #FF0000'; // 设置边框
        oDiv.style.position = 'absolute'; // 为新创建的DIV指定绝对定位
        oDiv.style.width = '30px'; // 指定宽度
        oDiv.style.height = '30px'; // 指定高度
        //oDiv.src = 'select.png';
        oDiv.style.opacity = '0.5'; //透明度
        oDiv.className = 'zhezhao';//加class 点刷新后删除遮罩
        document.body.appendChild(oDiv);
        
        //第四次点击后自动提交
        if (number == 4) {
            cheakOutTree();
        }
    
    });
    
})

//校验验证码
function cheakOutTree() {
    var txt = "";
    $("#codeSelect option").each(function (){  
          var text = $(this).text();  
          if(txt == ""){
              txt = text;
          }else{
              txt = txt + ";" + text;
          }
     });  
     $.ajax({  
        type:"post",      
        url:"http://localhost:8080/check",
        data : {"xy" : txt,"credential":"12345678"},
        cache : true,
        success : function(data) {
           alert(data.data);
           if (!data.result) {
               getCodeTree();
           }
        }
    }); 
}

function getNodePosition(node) {     
    var top = left = 0;
    while (node) {   
        if (node.tagName) {
            top = top + node.offsetTop;
            left = left + node.offsetLeft;   
            node = node.offsetParent;
        }
        else {
            node = node.parentNode;
        }
    } 
    return [left, top];
}        
</script>  
</body>
</html>
  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值