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>
三、运行