H5学习之路-图片验证码的实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qiyongkang520/article/details/54918151

验证码想必大家都经常遇到,今天就给大家介绍一个比较简单的图片验证码的实现。
要实现验证码主要分生成和验证两步。首先生成,就是指某个用户看到验证码后台立马生成的,然后把这个生成图片的字符串保存放在session或者内存中;要验证用户输入的字符串与生成的验证码是否一致,就是取出这个session中保存的字符串与用户输入的验证码进行比对即可。

一、html代码

笔者写了一个很简单的html界面,来简单模拟下过程,代码如下:

<!DOCTYPE html>
<html>
<head>
  <title>图片验证码</title>
  <meta charset="utf-8"/>
</head>
<body>
<input type="number" class="ui input row-1" placeholder="请输入验证码" id="yzm">
<a href="javascript:void(0)" class="refresh_yzm"><img src="" id="randomImg"></a>
<button type="button" id="save_btn">提交</button>

<script src="/m/plugins/zepto/zepto.min.js" type="text/javascript"></script>
<script src="../../scripts/captcha/imageCode.js" type="text/javascript"></script>
</body>
</html>

页面展示如下:
这里写图片描述

二、js代码

交互逻辑代码如下,比较简单,所以也用不着做啥介绍:

/**
 * 图片验证码
 */
var mobileKey = '';

$(function() {
  console.log('图片验证码。。。');

  getTicketImg();

  //点击验证码图片
  $('#randomImg').click(function() {
    getTicketImg();
  });

  //提交
  $('#save_btn').click(function() {
    //获取输入框的验证码
    var code = $('#yzm').val();
    console.log('code:' + code + ', mobileKey:' + mobileKey);

    var param = {
      code: code,
      mobileKey: mobileKey
    };
    $.post('/sys/captcha/validateImageCode.do', param, function(data) {
      console.log('data:', data);
      getTicketImg();
    });
  });
});

/**
 * 获取图片验证码
 */
function getTicketImg() { 
  var ticket_key = Math.floor(Math.random() * 1000000);
  mobileKey = ticket_key;
  $("#yzm").val("");
  var randomImg = "/sys/captcha/generateImageCode.do?mobileKey=" + ticket_key;
  $("#randomImg").attr("src", randomImg);
}

三、后台验证码图片生成以及验证

验证码图片生成的代码还是挺多的,这里使用的是patchca这个插件,版本是0.5.0的,大家可以在网上去下载,上传到自己私服的第三方库。
代码如下:

/**
 * Project Name:qyk_testSpringMVC
 * File Name:CaptchaController.java
 * Package Name:com.qiyongkang.sys.controller
 * Date:2017年2月7日上午9:49:30
 * Copyright (c) 2017, Thinkive(http://www.thinkive.com/) All Rights Reserved.
 *
*/

package com.qiyongkang.sys.controller;

import java.awt.Color;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.patchca.color.ColorFactory;
import org.patchca.filter.predefined.CurvesRippleFilterFactory;
import org.patchca.filter.predefined.DiffuseRippleFilterFactory;
import org.patchca.filter.predefined.DoubleRippleFilterFactory;
import org.patchca.filter.predefined.MarbleRippleFilterFactory;
import org.patchca.filter.predefined.WobbleRippleFilterFactory;
import org.patchca.service.ConfigurableCaptchaService;
import org.patchca.utils.encoder.EncoderHelper;
import org.patchca.word.RandomWordFactory;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.qiyongkang.sys.Constants;
import com.qiyongkang.sys.dto.ExtJsObject;

/**
 * ClassName:CaptchaController <br/>
 * Function: 验证码. <br/>
 * Date: 2017年2月7日 上午9:49:30 <br/>
 * 
 * @author qiyongkang
 * @version
 * @since JDK 1.6
 * @see
 */
@Controller
@RequestMapping
public class CaptchaController {

    /**
     * 日志类
     */
    private static Logger logger = LogManager.getLogger(CaptchaController.class);

    private static ConfigurableCaptchaService cs = new ConfigurableCaptchaService();

    private static int MAX_LENGTH = 4;

    private static int MIN_LENGTH = 4;

    private static Random random = new Random();

    private static Map<String, String> vcodeMap = new HashMap<String, String>();

    public static void saveVcode(String key, String vcode) {
        vcodeMap.put(key, vcode);
    }

    public static String getVcode(String key) {
        return vcodeMap.get(key);
    }

    public static void removeVcode(String key) {
        vcodeMap.remove(key);
    }

    public static void emptyAllVcode() {
        vcodeMap.clear();
    }

    static {
        cs.setColorFactory(new ColorFactory()  // 随机生成颜色
        {
            public Color getColor(int x) {
                int[] c = new int[3];
                int i = random.nextInt(c.length);
                for (int fi = 0; fi < c.length; fi++) {
                    if (fi == i) {
                        c[fi] = random.nextInt(71);
                    } else {
                        c[fi] = random.nextInt(256);
                    }
                }
                return new Color(c[0], c[1], c[2]);
            }
        });
        RandomWordFactory wf = new RandomWordFactory();
        wf.setCharacters("123456789");// 设置验证码的内容
        wf.setMaxLength(MAX_LENGTH);
        wf.setMinLength(MIN_LENGTH);
        cs.setWordFactory(wf);
    }

    /**
     * 
     * generateImageCode: 生成图片验证码. <br/>
     *
     * @author qiyongkang
     * @param request
     * @param response
     * @since JDK 1.6
     */
    @RequestMapping
    public void generateImageCode(HttpServletRequest request, HttpServletResponse response) {
        switch (random.nextInt(5)) {// 随机生成不同形式验证码
        case 0:
            cs.setFilterFactory(new CurvesRippleFilterFactory(cs.getColorFactory()));// 取现波纹
            break;
        case 1:
            cs.setFilterFactory(new MarbleRippleFilterFactory());// 大理石波纹
            break;
        case 2:
            cs.setFilterFactory(new DoubleRippleFilterFactory());// 双波纹
            break;
        case 3:
            cs.setFilterFactory(new WobbleRippleFilterFactory());// 摆波纹
            break;
        case 4:
            cs.setFilterFactory(new DiffuseRippleFilterFactory());// 双波纹
            break;
        }

        setResponseHeader(response); // 设置响应头

        HttpSession session = request.getSession(true);
        OutputStream os = null;
        String codeStr = "";
        try {
            os = response.getOutputStream();
            codeStr = EncoderHelper.getChallangeAndWriteImage(cs, "png", os);
        } catch (IOException e) {
            logger.error("生成验证码图片异常", e);
        } finally {
            if (os != null) {
                try {
                    os.flush();
                    os.close();
                } catch (IOException e) {
                    logger.error("关闭流异常", e);
                }

            }
        }

        logger.info("当前的SessionID=" + session.getId() + ", 验证码:" + codeStr);

        String key = request.getParameter("mobileKey");
        if (key == null) {
            key = "";
        }
        //保存到map中
        saveVcode(key, codeStr);

        //放在session中
        session.setAttribute(Constants.TICKET + key, codeStr);
    }

    /**
     * 
     * validateImageCode: 校验图片验证码. <br/>
     *
     * @author qiyongkang
     * @since JDK 1.6
     */
    @RequestMapping
    @ResponseBody
    public ExtJsObject validateImageCode(HttpServletRequest request) {
        ExtJsObject extJsObject = new ExtJsObject();

        //code
        String code = request.getParameter("code");

        //mobileKey
        String mobileKey = request.getParameter("mobileKey");

        if (StringUtils.isEmpty(code) || StringUtils.isEmpty(mobileKey)) {
            extJsObject.setSuccess(false);
            extJsObject.setMsg("验证码不能为空");
        } else {
            //判断验证码是否正确, 两重判断,session中和map集合中的
            HttpSession session = request.getSession();
            if (code.equalsIgnoreCase(session.getAttribute(Constants.TICKET + mobileKey).toString()) 
                    && code.equalsIgnoreCase(CaptchaController.getVcode(mobileKey))) {
                CaptchaController.removeVcode(mobileKey);

                extJsObject.setSuccess(true);
                extJsObject.setMsg("验证码正确");
            } else {
                extJsObject.setSuccess(false);
                extJsObject.setMsg("验证码错误");
            }
        }

        return extJsObject;
    }

    /**
     * 设置输出流响应头
     */
    private void setResponseHeader(HttpServletResponse response) {
        response.setContentType("image/png");
        response.setHeader("cache", "no-cache");
        response.setContentType("image/png");
        response.setHeader("Cache-Control", "no-cache, no-store");
        response.setHeader("Pragma", "no-cache");
        long time = System.currentTimeMillis();
        response.setDateHeader("Last-Modified", time);
        response.setDateHeader("Date", time);
        response.setDateHeader("Expires", time);
    }
}

这里因为在前端会带一个随机码参数过来,所以做了双重验证,更加安全;另外,大家还可以写一个定时任务,每天清一次map中的所有key,防止占用过多的内存;好了,就简单介绍到这儿了,大家可以去试一试!

猜你喜欢

转载自blog.csdn.net/qiyongkang520/article/details/54918151