Spring Boot中验证码实现kaptcha
要生成验证码网上的方案比较多,基本是基于两大类:1为自定义生成,操作用Image类,2为kaptcha生成,有模糊算法。
当然也可以直接交由前端进行处理
1、基于kaptcha
首先不要怀疑的是报名是GitHub还是Google的,因为都是出自私人的产物,并且之前在Google Code进行托管,然后关闭了Google Code之后,有人就把其转入到GitHub中,并且不只一个人,所以才会造成市面上这么多不同的报名。
但总体功能基本不变。
下面POM先引入包:
<!-- https://mvnrepository.com/artifact/com.github.penggle/kaptcha --> <dependency> <groupId>com.github.penggle</groupId> <artifactId>kaptcha</artifactId> <version>2.3.2</version> </dependency>
这个包也不要怀疑,在GitHub上的星星是最多的。
KaptchaConfig.java
import java.util.Properties; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import com.google.code.kaptcha.impl.DefaultKaptcha; import com.google.code.kaptcha.util.Config; @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", "yes"); properties.setProperty("kaptcha.border.color", "105,179,90"); properties.setProperty("kaptcha.textproducer.font.color", "blue"); properties.setProperty("kaptcha.image.width", "110"); properties.setProperty("kaptcha.image.height", "40"); properties.setProperty("kaptcha.textproducer.font.size", "30"); properties.setProperty("kaptcha.session.key", "code"); properties.setProperty("kaptcha.textproducer.char.length", "4"); properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑"); Config config = new Config(properties); defaultKaptcha.setConfig(config); return defaultKaptcha; } }
这个配置文件可以写成XML,然后通过在main方法使用@ImportResource进行导入XML配置:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="captchaProducer" class="com.google.code.kaptcha.impl.DefaultKaptcha"> <property name="config"> <bean class="com.google.code.kaptcha.util.Config"> <constructor-arg type="java.util.Properties"> <props> <prop key = "kaptcha.border ">yes</prop> <prop key="kaptcha.border.color">105,179,90</prop> <prop key="kaptcha.textproducer.font.color">blue</prop> <prop key="kaptcha.image.width">100</prop> <prop key="kaptcha.image.height">50</prop> <prop key="kaptcha.textproducer.font.size">27</prop> <prop key="kaptcha.session.key">code</prop> <prop key="kaptcha.textproducer.char.length">4</prop> <prop key="kaptcha.textproducer.font.names">宋体,楷体,微软雅黑</prop> <prop key="kaptcha.textproducer.char.string">0123456789ABCEFGHIJKLMNOPQRSTUVWXYZ</prop> <prop key="kaptcha.obscurificator.impl">com.google.code.kaptcha.impl.WaterRipple</prop> <prop key="kaptcha.noise.color">black</prop> <prop key="kaptcha.noise.impl">com.google.code.kaptcha.impl.DefaultNoise</prop> <prop key="kaptcha.background.clear.from">185,56,213</prop> <prop key="kaptcha.background.clear.to">white</prop> <prop key="kaptcha.textproducer.char.space">3</prop> </props> </constructor-arg> </bean> </property> </bean> </beans>
@SpringBootApplication @ImportResource(locations={"classpath:mykaptcha.xml"}) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
注意:以上的main方法是导入XML文件的写法,普通注解方式不需要这么写。普通即可。
Controller,生成验证码
@Autowired DefaultKaptcha defaultKaptcha; @RequestMapping("/defaultKaptcha") public void defaultKaptcha(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse) throws Exception{ byte[] captchaChallengeAsJpeg = null; ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream(); try { //生产验证码字符串并保存到session中 String createText = defaultKaptcha.createText(); httpServletRequest.getSession().setAttribute("vrifyCode", createText); //使用生产的验证码字符串返回一个BufferedImage对象并转为byte写入到byte数组中 BufferedImage challenge = defaultKaptcha.createImage(createText); ImageIO.write(challenge, "jpg", jpegOutputStream); } catch (IllegalArgumentException e) { httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND); return; } //定义response输出类型为image/jpeg类型,使用response输出流输出图片的byte数组 captchaChallengeAsJpeg = jpegOutputStream.toByteArray(); httpServletResponse.setHeader("Cache-Control", "no-store"); httpServletResponse.setHeader("Pragma", "no-cache"); httpServletResponse.setDateHeader("Expires", 0); httpServletResponse.setContentType("image/jpeg"); ServletOutputStream responseOutputStream = httpServletResponse.getOutputStream(); responseOutputStream.write(captchaChallengeAsJpeg); responseOutputStream.flush(); responseOutputStream.close(); }
验证方法
@RequestMapping("/imgvrifyControllerDefaultKaptcha") public ModelAndView imgvrifyControllerDefaultKaptcha(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse){ ModelAndView andView = new ModelAndView(); String captchaId = (String) httpServletRequest.getSession().getAttribute("vrifyCode"); String parameter = httpServletRequest.getParameter("vrifyCode"); System.out.println("Session vrifyCode "+captchaId+" form vrifyCode "+parameter); if (!captchaId.equals(parameter)) { andView.addObject("info", "错误的验证码"); andView.setViewName("index"); } else { andView.addObject("info", "登录成功"); andView.setViewName("succeed"); } return andView; }
配置项参考:
kaptcha.border 是否有边框 默认为true 我们可以自己设置yes,no kaptcha.border.color 边框颜色 默认为Color.BLACK kaptcha.border.thickness 边框粗细度 默认为1 kaptcha.producer.impl 验证码生成器 默认为DefaultKaptcha kaptcha.textproducer.impl 验证码文本生成器 默认为DefaultTextCreator kaptcha.textproducer.char.string 验证码文本字符内容范围 默认为abcde2345678gfynmnpwx kaptcha.textproducer.char.length 验证码文本字符长度 默认为5 kaptcha.textproducer.font.names 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) kaptcha.textproducer.font.size 验证码文本字符大小 默认为40 kaptcha.textproducer.font.color 验证码文本字符颜色 默认为Color.BLACK kaptcha.textproducer.char.space 验证码文本字符间距 默认为2 kaptcha.noise.impl 验证码噪点生成对象 默认为DefaultNoise kaptcha.noise.color 验证码噪点颜色 默认为Color.BLACK kaptcha.obscurificator.impl 验证码样式引擎 默认为WaterRipple kaptcha.word.impl 验证码文本字符渲染 默认为DefaultWordRenderer kaptcha.background.impl 验证码背景生成器 默认为DefaultBackground kaptcha.background.clear.from 验证码背景颜色渐进 默认为Color.LIGHT_GRAY kaptcha.background.clear.to 验证码背景颜色渐进 默认为Color.WHITE kaptcha.image.width 验证码图片宽度 默认为200 kaptcha.image.height 验证码图片高度 默认为50
2、自定义方案
验证码生成类
import java.awt.*; import java.awt.image.BufferedImage; import java.io.OutputStream;import java.util.HashMap; import java.util.Map;import java.util.Random; public class ImageCode { private static char mapTable[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; public static Map<String, Object> getImageCode(int width, int height, OutputStream os) { Map<String,Object> returnMap = new HashMap<String, Object>(); if (width <= 0) width = 60; if (height <= 0) height = 20; BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); // 获取图形上下文 Graphics g = image.getGraphics(); //生成随机类 Random random = new Random(); // 设定背景色 g.setColor(getRandColor(200, 250)); g.fillRect(0, 0, width, height); //设定字体 g.setFont(new Font("Times New Roman", Font.PLAIN, 18)); // 随机产生168条干扰线,使图象中的认证码不易被其它程序探测到 g.setColor(getRandColor(160, 200)); for (int i = 0; i < 168; i++) { int x = random.nextInt(width); int y = random.nextInt(height); int xl = random.nextInt(12); int yl = random.nextInt(12); g.drawLine(x, y, x + xl, y + yl); } //取随机产生的码 String strEnsure = ""; //4代表4位验证码,如果要生成更多位的认证码,则加大数值 for (int i = 0; i < 4; ++i) { strEnsure += mapTable[(int) (mapTable.length * Math.random())]; // 将认证码显示到图象中 g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110))); //直接生成 String str = strEnsure.substring(i, i + 1); g.drawString(str, 13 * i + 6, 16); } // 释放图形上下文 g.dispose(); returnMap.put("image",image); returnMap.put("strEnsure",strEnsure); return returnMap; } //给定范围获得随机颜色 static Color getRandColor(int fc, int bc) { Random random = new Random(); if (fc > 255) fc = 255; if (bc > 255) bc = 255; int r = fc + random.nextInt(bc - fc); int g = fc + random.nextInt(bc - fc); int b = fc + random.nextInt(bc - fc); return new Color(r, g, b); } }
获取验证码API
@RequestMapping(value = "/images/imagecode") public String imagecode(HttpServletRequest request, HttpServletResponse response) throws Exception { OutputStream os = response.getOutputStream(); Map<String,Object> map = ImageCode.getImageCode(60, 20, os); String simpleCaptcha = "simpleCaptcha"; request.getSession().setAttribute(simpleCaptcha, map.get("strEnsure").toString().toLowerCase()); request.getSession().setAttribute("codeTime",new Date().getTime()); try { ImageIO.write((BufferedImage) map.get("image"), "JPEG", os); } catch (IOException e) { return ""; } return null; }
验证验证码API
@RequestMapping(value = "/checkcode") @ResponseBody public String checkcode(HttpServletRequest request, HttpSession session) throws Exception { String checkCode = request.getParameter("checkCode"); Object cko = session.getAttribute("simpleCaptcha") ; //验证码对象 if(cko == null){ request.setAttribute("errorMsg", "验证码已失效,请重新输入!"); return "验证码已失效,请重新输入!"; } String captcha = cko.toString(); Date now = new Date(); Long codeTime = Long.valueOf(session.getAttribute("codeTime")+""); if(StringUtils.isEmpty(checkCode) || captcha == null || !(checkCode.equalsIgnoreCase(captcha))) { request.setAttribute("errorMsg", "验证码错误!"); return "验证码错误!"; } else if ((now.getTime()-codeTime)/1000/60>5) { //验证码有效时长为5分钟 request.setAttribute("errorMsg", "验证码已失效,请重新输入!"); return "验证码已失效,请重新输入!"; }else { session.removeAttribute("simpleCaptcha"); return "1"; } }
参考:
http://blog.csdn.net/u014104286/article/details/70515004
http://blog.csdn.net/liunian02050328/article/details/53462053
http://www.jb51.net/article/115800.htm
http://blog.csdn.net/rambo_china/article/details/7720181