一 简介

Kaptcha是一个基于SimpleCaptcha的验证码开源项目,在我们的项目中使用Kaptcha组件可以快速生成比较安全的验证码。同时Kaptcha还提供了许多的参数可以让我们自定义生成的验证码样式

jar包的官网下载地址:https://code.google.com/archive/p/kaptcha/downloads

当然,为了照顾一些翻不了墙的同学,我也在51cto上上传了一份Kaptcha最新的的jar包(kaptcha-2.3.2),传送门:http://down.51cto.com/data/2257293

二 代码实现

(1)在项目中添加Kaptcha相关jar包:

具体就是:

kaptcha-2.3.2.jar

(2)在spring的配置文件中添加Kaptcha相关的配置:

<!--Kaptcha验证码生成器--><beanid="captchaProducer"class="com.google.code.kaptcha.impl.DefaultKaptcha"><propertyname="config"><beanclass="com.google.code.kaptcha.util.Config"><constructor-arg><props><propkey="kaptcha.border">no</prop><!--是否有边框--><propkey="kaptcha.noise.color">25,25,25</prop><!--干扰线颜色--><propkey="kaptcha.obscurificator.impl">com.google.code.kaptcha.impl.ShadowGimpy</prop><propkey="kaptcha.p_w_picpath.width">140</prop><propkey="kaptcha.p_w_picpath.height">40</prop><propkey="kaptcha.textproducer.char.string">AZWSXEDCRFVTGBYHNUJMIKLP23456789</prop><propkey="kaptcha.textproducer.font.color">4,14,156</prop><!--验证码字体颜色--><propkey="kaptcha.textproducer.font.size">36</prop><!--验证码字体大小--><propkey="kaptcha.session.key">code</prop><propkey="kaptcha.textproducer.char.length">5</prop><!--验证码个数--><propkey="kaptcha.textproducer.font.names">宋体,楷体,微软雅黑</prop></props></constructor-arg></bean></property></bean>

注:其他的一些可选配置:

kaptcha.border是否有边框默认为true我们可以自己设置yes,nokaptcha.border.color边框颜色默认为Color.BLACKkaptcha.border.thickness边框粗细度默认为1kaptcha.producer.impl验证码生成器默认为DefaultKaptchakaptcha.textproducer.impl验证码文本生成器默认为DefaultTextCreatorkaptcha.textproducer.char.string验证码文本字符内容范围默认为abcde2345678gfynmnpwxkaptcha.textproducer.char.length验证码文本字符长度默认为5kaptcha.textproducer.font.names验证码文本字体样式默认为newFont("Arial",1,fontSize),newFont("Courier",1,fontSize)kaptcha.textproducer.font.size验证码文本字符大小默认为40kaptcha.textproducer.font.color验证码文本字符颜色默认为Color.BLACKkaptcha.textproducer.char.space验证码文本字符间距默认为2kaptcha.noise.impl验证码噪点生成对象默认为DefaultNoisekaptcha.noise.color验证码噪点颜色默认为Color.BLACKkaptcha.obscurificator.impl验证码样式引擎默认为WaterRipplekaptcha.word.impl验证码文本字符渲染默认为DefaultWordRendererkaptcha.background.impl验证码背景生成器默认为DefaultBackgroundkaptcha.background.clear.from验证码背景颜色渐进默认为Color.LIGHT_GRAYkaptcha.background.clear.to验证码背景颜色渐进默认为Color.WHITEkaptcha.p_w_picpath.width验证码图片宽度默认为200kaptcha.p_w_picpath.height验证码图片高度默认为50

(3)在Controller中分别添加用于生成验证码以及校验验证码的controller:

packagecn.zifangsky.controller;importjava.awt.p_w_picpath.BufferedImage;importjava.io.IOException;importjava.util.HashMap;importjava.util.Map;importjavax.p_w_picpathio.ImageIO;importjavax.servlet.ServletOutputStream;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjavax.servlet.http.HttpSession;importorg.apache.commons.lang3.StringUtils;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Controller;importorg.springframework.web.bind.annotation.RequestBody;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RequestMethod;importorg.springframework.web.bind.annotation.ResponseBody;importcom.google.code.kaptcha.Constants;importcom.google.code.kaptcha.Producer;importcn.zifangsky.model.VerifyCode;@ControllerpublicclassCaptchaController{@AutowiredprivateProducerproducer;/***生成验证码*@paramrequest*@paramresponse*/@RequestMapping("/user/user/verify.html")publicvoidgenerate(HttpServletRequestrequest,HttpServletResponseresponse){response.setDateHeader("Expires",0);response.setHeader("Cache-Control","no-store,no-cache,must-revalidate");response.addHeader("Cache-Control","post-check=0,pre-check=0");response.setHeader("Pragma","no-cache");response.setContentType("p_w_picpath/jpeg");StringvalidateText=producer.createText();//生成验证码文字//存储到session中request.getSession().setAttribute(Constants.KAPTCHA_SESSION_KEY,validateText);BufferedImagebImage=producer.createImage(validateText);try{ServletOutputStreamoutputStream=response.getOutputStream();ImageIO.write(bImage,"jpg",outputStream);outputStream.flush();outputStream.close();}catch(IOExceptione){e.printStackTrace();}}/***校验验证码*@return返回是否校验成功*/@RequestMapping(value="/user/user/checkVerifyCode.json",method={RequestMethod.POST})@ResponseBodypublicMap<String,String>check(@RequestBodyVerifyCodeverifyCode,HttpServletRequestrequest){HttpSessionsession=request.getSession();Map<String,String>result=newHashMap<>();//session中的验证码StringcodeFromSession=(String)session.getAttribute(Constants.KAPTCHA_SESSION_KEY);session.removeAttribute(Constants.KAPTCHA_SESSION_KEY);//使用之后删除if(StringUtils.isNotBlank(verifyCode.getVerifyCodeValue())&&StringUtils.isNotBlank(codeFromSession)){if(verifyCode.getVerifyCodeValue().equalsIgnoreCase(codeFromSession)){session.setAttribute("codeCheck",true);//设置校验成功标志result.put("result","ok");}else{result.put("result","error");}}else{result.put("result","error");}returnresult;}}

注:这里使用json请求异步校验验证时使用了Jackson来将前台传递过来的json字符串自动转换成Java对象,因此如果像我这里采用异步校验的话还需要配置Jackson相关的配置,具体配置如下:

i)VerifyCode.java

packagecn.zifangsky.model;publicclassVerifyCode{privateStringverifyCodeValue;publicStringgetVerifyCodeValue(){returnverifyCodeValue;}publicvoidsetVerifyCodeValue(StringverifyCodeValue){this.verifyCodeValue=verifyCodeValue;}}

ii)向项目中引入Jackson相关的jar包:

下载地址如下:http://down.51cto.com/data/2257291

iii)在SpringMVC的配置文件中添加以下配置:

<beanid="mappingJacksonHttpMessageConverter"class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"><propertyname="supportedMediaTypes"><list><value>text/html;charset=UTF-8</value><value>application/json;charset=UTF-8</value></list></property><propertyname="objectMapper"><beanclass="org.codehaus.jackson.map.ObjectMapper"><propertyname="dateFormat"><beanclass="java.text.SimpleDateFormat"><constructor-argtype="java.lang.String"value="yyyy-MM-ddHH:mm:ss"></constructor-arg></bean></property></bean></property></bean><!--启动SpringMVC的注解功能,完成请求和注解POJO的映射--><beanclass="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"><propertyname="messageConverters"><list><refbean="mappingJacksonHttpMessageConverter"/><!--json转换器--></list></property></bean><mvc:annotation-drivencontent-negotiation-manager="contentNegotiationManager"/><beanid="contentNegotiationManager"class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean"><!--true,开启扩展名支持,false关闭支持--><propertyname="favorPathExtension"value="false"/><!--用于开启/userinfo/123?format=json的支持--><propertyname="favorParameter"value="true"/><!--设置为true以忽略对AcceptHeader的支持--><propertyname="ignoreAcceptHeader"value="false"/><propertyname="mediaTypes"><value>atom=application/atom+xmlhtml=text/htmljson=application/jsonxml=application/xml*=*/*</value></property></bean>

(4)登录页面:

<%@pageimport="java.security.SecureRandom"%><%@pagelanguage="java"contentType="text/html;charset=UTF-8"pageEncoding="UTF-8"%><%@taglibprefix="c"uri="http://java.sun.com/jsp/jstl/core"%><html><head><metahttp-equiv="Content-Type"content="text/html;charset=UTF-8"><c:setvar="path"value="${pageContext.request.contextPath}"/><title>ShiroDemo2</title><scriptsrc="${path}/scripts/jquery/jquery-1.9.0.min.js"type="text/javascript"></script><scripttype="text/javascript">$(function(){$("#verifyImg").click(function(){$("#message").text("");$(this).attr("src","<c:urlvalue='/user/user/verify.html'/>?"+Math.floor(Math.random()*100));});$("#verifyCode").keyup(function(){varverifyCodeValue=$("#verifyCode").val().replace("/\s/g","");vardata={"verifyCodeValue":verifyCodeValue};if(verifyCodeValue.length==5){$.ajax({url:"<c:urlvalue='/user/user/checkVerifyCode.json'/>",type:"POST",contentType:'application/json;charset=utf-8',data:JSON.stringify(data),success:function(response){if(response.result=="ok"){$("#message").text("ok");$("#password").focus();}elseif(response.result=="error"){$("#message").text("error");$("#verifyImg").attr("src","<c:urlvalue='/user/user/verify.html'/>?"+Math.floor(Math.random()*100));}},error:function(response){alert("提交失败,请重新提交");}});}});$("#submit").click(function(){varaction="<c:urlvalue='/user/user/check.json'/>";vardata={"username":$("#username").val(),"password":$("#password").val()};$.ajax({url:action,type:"POST",contentType:'application/json',data:JSON.stringify(data),success:function(response){if(response.result=="success"){window.location.href="<c:urlvalue='/user/index.html'/>";}elseif(response.result=="error"){$("#error").text("登录失败,请重新提交");$("#verifyImg").attr("src","<c:urlvalue='/user/user/verify.html'/>?"+Math.floor(Math.random()*100));}},error:function(response){alert("提交失败,请重新提交");}});});});</script><STYLEtype="text/css">#login{width:400px;height:280px;position:absolute;left:50%;top:50%;margin-left:-200px;margin-top:-140px;border:1px;align:center;}#form{width:300px;height:160px;position:relative;left:50%;top:50%;margin-left:-150px;margin-top:-80px;text-align:center";}</STYLE></head><body><divid="login"><divid="form"><formid="myform"><div><inputtype="text"id="username"name="username"placeholder="用户名"/><inputtype="password"id="password"name="password"placeholder="密码"/></div><div><inputtype="text"id="verifyCode"name="verifyCode"maxlength="5"placeholder="验证码"/><imgid="verifyImg"name="verifyImg"title="点击刷新"src="<c:urlvalue='/user/user/verify.html'/>"><spanid="message"></span></div><divid="submit"><inputtype="submit"value="登录"/><divid="error"></div></div></form></div></div></body></html>

前台登录页面这里就不做过多解释了,都是jQuery的一些写法。代码不难,如果了解jQuery的一些基本用法的话,理解上面的代码应该挺容易的。当然,最后进行登录校验时需要判断session中是否有“codeCheck”这个参数,用于判断验证码是否已经校验成功过。具体的代码如下:

/***登录校验**@paramUsrUser*登录时的User对象,包括form表单中的用户名和密码*@paramrequest*@return是否登录成功标志*/@RequestMapping(value="/user/user/check.json",method={RequestMethod.POST})@ResponseBodypublicMap<String,String>loginCheck(@RequestBodyUsrUseruser,HttpServletRequestrequest){HttpSessionsession=request.getSession();Map<String,String>result=newHashMap<>();BooleancodeCheck=(Boolean)session.getAttribute("codeCheck");//验证码是否校验成功标志session.removeAttribute("codeCheck");if(codeCheck!=null&&codeCheck){UsrUserBOuserBO=userManager.login(user.getUsername(),user.getPassword());if(userBO!=null){session.setAttribute("userBO",userBO);//登录成功之后加入session中result.put("result","success");}else{result.put("result","error");}}else{result.put("result","error");}returnresult;}

(5)最后生成的验证码效果如下:

参考文章:

https://my.oschina.net/u/1450300/blog/486377

PS:上面图片中的水印是我个人博客的域名,因此还请管理员手下留情不要给我标为“转载文章”,谢谢!!!