springboot 生成验证码

概述

场景介绍 验证码,用于 web 网站。用户点击验证码图片后,生成验证码。提交后,用户输入验证码和 Session 验证码,进行校验。

一、依赖

    </dependencies>
        <dependency>
            <groupId>com.github.penggle</groupId>
            <artifactId>kaptcha</artifactId>
            <version>2.3.2</version>
        </dependency>
    </dependencies>

二、功能讲解

(1)验证码配置文件

package com.chinamobile.config;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.util.Properties;


/**
 * Description
 *
 * @author <a href="https://qiankunpingtai.cn">qiankunpingtai</a>
 * @Date: 2019/7/10 11:00
 */
@Component
public class KaptchaConfig {
    @Bean
    public DefaultKaptcha getDefaultKaptcha() {
        com.google.code.kaptcha.impl.DefaultKaptcha defaultKaptcha = new com.google.code.kaptcha.impl.DefaultKaptcha();
        Properties properties = new Properties();
        // 图片边框
        properties.setProperty("kaptcha.border", "no");
        // 边框颜色
        properties.setProperty("kaptcha.border.color", "black");
        //边框厚度
        properties.setProperty("kaptcha.border.thickness", "1");
        // 图片宽
        properties.setProperty("kaptcha.image.width", "200");
        // 图片高
        properties.setProperty("kaptcha.image.height", "50");
        //图片实现类
        properties.setProperty("kaptcha.producer.impl", "com.google.code.kaptcha.impl.DefaultKaptcha");
        //文本实现类
        properties.setProperty("kaptcha.textproducer.impl", "com.google.code.kaptcha.text.impl.DefaultTextCreator");
        //文本集合,验证码值从此集合中获取
        properties.setProperty("kaptcha.textproducer.char.string", "01234567890");
        //验证码长度
        properties.setProperty("kaptcha.textproducer.char.length", "4");
        //字体
        properties.setProperty("kaptcha.textproducer.font.names", "宋体");
        //字体颜色
        properties.setProperty("kaptcha.textproducer.font.color", "black");
        //文字间隔
        properties.setProperty("kaptcha.textproducer.char.space", "5");
        //干扰实现类
        properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.DefaultNoise");
        //干扰颜色
        properties.setProperty("kaptcha.noise.color", "blue");
        //干扰图片样式
        properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.WaterRipple");
        //背景实现类
        properties.setProperty("kaptcha.background.impl", "com.google.code.kaptcha.impl.DefaultBackground");
        //背景颜色渐变,结束颜色
        properties.setProperty("kaptcha.background.clear.to", "white");
        //文字渲染器
        properties.setProperty("kaptcha.word.impl", "com.google.code.kaptcha.text.impl.DefaultWordRenderer");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }

}

(2)生成验证码方法

package com.chinamobile.util;

import com.google.code.kaptcha.impl.DefaultKaptcha;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;

/**
 * Description
 *
 * @author <a href="https://qiankunpingtai.cn">qiankunpingtai</a>
 * @Date: 2019/7/10 11:09
 */
public class KaptchaUtil {
    /**
     * 生成验证码图片
     * @param request 设置session
     * @param response 转成图片
     * @param captchaProducer 生成图片方法类
     * @param validateSessionKey session名称
     * @throws Exception
     */
    public static void validateCode(HttpServletRequest request, HttpServletResponse response, DefaultKaptcha captchaProducer, String validateSessionKey) throws Exception{
        // Set to expire far in the past.
        response.setDateHeader("Expires", 0);
        // Set standard HTTP/1.1 no-cache headers.
        response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
        // Set IE extended HTTP/1.1 no-cache headers (use addHeader).
        response.addHeader("Cache-Control", "post-check=0, pre-check=0");
        // Set standard HTTP/1.0 no-cache header.
        response.setHeader("Pragma", "no-cache");

        // return a jpeg
        response.setContentType("image/jpeg");

        // create the text for the image
        String capText = captchaProducer.createText();

        // store the text in the session
        request.getSession().setAttribute(validateSessionKey, capText);

        // create the image with the text
        BufferedImage bi = captchaProducer.createImage(capText);

        ServletOutputStream out = response.getOutputStream();

        // write the data out
        ImageIO.write(bi, "jpg", out);
        try {
            out.flush();
        } finally {
            out.close();
        }
    }

    /**
     * 生成验证码字符串
     */
    public static String getvalidateCodeTextKey(HttpServletRequest request, HttpServletResponse response, DefaultKaptcha captchaProducer) throws Exception{
        String capText = captchaProducer.createText();
        String sessionId=request.getSession().getId();
        ConfigConstants.getvalidateCodeMap().put(sessionId,capText);
        return sessionId;

    }

}

(3)验证码控制类

package com.chinamobile.constants;

/**
 * Description
 *  配置参数常量类
 * @author <a href="https://qiankunpingtai.cn">qiankunpingtai</a>
 * @Date: 2019/6/17 16:38
 */
public class ConfigConstants {
    /**
     * 登录验证码SessionKey
     */
    public static final String LOGIN_VALIDATE_CODE = "login_validate_code";

    /**
     * 验证码存储
     * */
    public volatile static Map<String,String> VALIDATE_CODE_MAP;
    public  static final String VALIDATE_CODE_MAP_STR=new String("validate_code");
    public static Map<String,String> getvalidateCodeMap() {
        if (VALIDATE_CODE_MAP == null) {
            synchronized (VALIDATE_CODE_MAP_STR) {
                if (VALIDATE_CODE_MAP == null) {
                    VALIDATE_CODE_MAP = new ConcurrentHashMap<>();
                }
            }
        }
        return VALIDATE_CODE_MAP;
    }


}

package com.chinamobile.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.chinamobile.constants.ConfigConstants;
import com.chinamobile.constants.ExceptionConstants;
import com.chinamobile.util.KaptchaUtil;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;


@Controller
@RequestMapping("/logon")
public class LogonController {
	@Resource
	private DefaultKaptcha captchaProducer;
	/**
	 * 登录验证码图片
	 */
	@RequestMapping(value = {"/loginValidateCode"})
	public void loginValidateCode(HttpServletRequest request, HttpServletResponse response) throws Exception{
		KaptchaUtil.validateCode(request,response,captchaProducer, ConfigConstants.LOGIN_VALIDATE_CODE);
	}
	/**
	 * 检查验证码是否正确
	 */
	@RequestMapping("/checkLoginValidateCode")
	@ResponseBody
	public String checkLoginValidateCode(HttpServletRequest request, @RequestParam("validateCode")String validateCode) {
		String loginValidateCode = request.getSession().getAttribute(ConfigConstants.LOGIN_VALIDATE_CODE).toString();
		Map<String, Object> resultMap = new HashMap<String, Object>();
		if(loginValidateCode == null){
			resultMap.put(ExceptionConstants.GLOBAL_RETURNS_DATA,null);//验证码过期
		}else if(loginValidateCode.equals(validateCode)){
			resultMap.put(ExceptionConstants.GLOBAL_RETURNS_DATA,true);//验证码正确
		}else if(!loginValidateCode.equals(validateCode)){
			resultMap.put(ExceptionConstants.GLOBAL_RETURNS_DATA,false);//验证码不正确
		}
		resultMap.put(ExceptionConstants.GLOBAL_RETURNS_MESSAGE,ExceptionConstants.SERVICE_SUCCESS_MSG);
		resultMap.put(ExceptionConstants.GLOBAL_RETURNS_CODE,ExceptionConstants.SERVICE_SUCCESS_CODE);
		return  JSON.toJSONString(resultMap, SerializerFeature.WriteMapNullValue);
	}

	// 测试用,返回成功
	@RequestMapping("/login")
	@ResponseBody
	public Map<String, Object> login(HttpServletResponse response, HttpServletRequest request, String username,
			String password,@RequestParam("validateCodeKey")String validateCodeKey,@RequestParam("validateCodeText")String validateCodeText) {
		User user = null;
		if(!ConfigConstants.getvalidateCodeMap().containsKey(validateCodeKey)){
			throw new BusinessRunTimeException(ExceptionConstants.VALIDATE_CODE_IS_NOT_FOUND_CODE,
					ExceptionConstants.VALIDATE_CODE_IS_NOT_FOUND_MSG);
		}
		String loginValidateCode=ConfigConstants.getvalidateCodeMap().get(validateCodeKey);
		//校验验证码
		if(loginValidateCode == null){
			//验证码已过期
			throw new BusinessRunTimeException(ExceptionConstants.VALIDATE_CODE_OUT_OF_DATE_CODE,
					ExceptionConstants.VALIDATE_CODE_OUT_OF_DATE_MSG);
		}else if(!loginValidateCode.equals(validateCodeText)){
			//验证码不正确
			throw new BusinessRunTimeException(ExceptionConstants.VALIDATE_CODE_ERROR_CODE,
					ExceptionConstants.VALIDATE_CODE_ERROR_MSG);
		}
		//从内存中移除验证码
		ConfigConstants.getvalidateCodeMap().remove(validateCodeKey);
                /**
                  * 用户信息验证
                   */
                     ......
	}


}

(4)异常类

package com.chinamobile.constants;

import com.alibaba.fastjson.JSONObject;

/**
 * Description
 *  异常定义常量类
 * @author <a href="https://qiankunpingtai.cn">qiankunpingtai</a>
 * @Date: 2019/6/17 16:38
 */
public class ExceptionConstants {


    public static final String GLOBAL_RETURNS_CODE = "code";
    public static final String GLOBAL_RETURNS_MESSAGE = "cnmessage";
    public static final String GLOBAL_RETURNS_DATA = "data";

    /**
     * 正常返回/操作成功
     **/
    public static final int SERVICE_SUCCESS_CODE = 200;
    public static final String SERVICE_SUCCESS_MSG = "操作成功";


    /**
     * 系统运行时未知错误
     **/
    public static final int SERVICE_SYSTEM_ERROR_CODE = 500;
    public static final String SERVICE_SYSTEM_ERROR_MSG = "未知异常";
    public static  final  String SYSTEM_IDENTIFICATION_ERROE = "身份认证失败";

    //验证码错误
    public static final int VALIDATE_CODE_ERROR_CODE = 706;
    public static final String VALIDATE_CODE_ERROR_MSG = "验证码错误";
    //验证码已过期
    public static final int VALIDATE_CODE_OUT_OF_DATE_CODE = 707;
    public static final String VALIDATE_CODE_OUT_OF_DATE_MSG = "验证码已过期";
    //验证码未找到
    public static final int VALIDATE_CODE_IS_NOT_FOUND_CODE = 708;
    public static final String VALIDATE_CODE_IS_NOT_FOUND_MSG = "验证码未找到";

    
    /**
     * 标准正常返回/操作成功返回
     * @return
     */
    public static JSONObject standardSuccess () {
        JSONObject success = new JSONObject();
        success.put(GLOBAL_RETURNS_CODE, SERVICE_SUCCESS_CODE);
        success.put(GLOBAL_RETURNS_MESSAGE, SERVICE_SUCCESS_MSG);
        return success;
    }


}

(5)前端页面

<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>SpringBoot 生成验证码</title>
<script type="text/javascript" src="/js/jquery/jquery-3.3.1.min.js"></script>
<script type='text/javascript'>

$(function () {
    $("#validateCode").keyup(function(){
        checkLoginValidateCode($(this).val());
    });

});

function uploadLoginValidateCode() {
    $("#loginValidateCode").attr("src","/logon/loginValidateCode?random="+new Date().getMilliseconds());
}


function checkLoginValidateCode(validateCode) {
    var error = $("#validateCode").parent().next();
    if(validateCode != null && validateCode != ""){
        $.ajax({
            type: "POST",
            async:false,
            url: "/logon/checkLoginValidateCode?validateCode="+validateCode,
            success : function(json) {
                if(json != null && json.code == 200 && json.status != null) {
                    if (json.status == true) {
                        error.html("恭喜你验证码,正确!!!!!");
                    } else if(json.status == false){
                        error.html("验证码错误,请重新输入");
                    }else{
                        error.html("验证码过期,请重新输入");
                        uploadLoginValidateCode();
                    }
                }
                return false;
            },
            error:function(XMLHttpRequest,textStatus,errorThrown){
                alert("服务器错误!状态码:"+XMLHttpRequest.status);
                // 状态
                console.log(XMLHttpRequest.readyState);
                // 错误信息
                console.log(textStatus);
                return false;
            }
        });
    }else{
        error.html("请输入验证码!");
    }
}

</script>
</head>
<body>
验证码: <img id="loginValidateCode" height="40" width="150"  style="cursor: pointer;" src="/logon/loginValidateCode" onclick="uploadLoginValidateCode();">
<p>
    你输入的内容:<input type="text" id="validateCode" name="validateCode" />
</p>
<p style="color: red"></p>
</body>
</html>

三、运行

访问链接:http://127.0.0.1:8080

1.png

2.png

3.png

4.png