我们看到很多网站上都有验证码的功能,因为他们需要防止机器人注册等等,导致数据膨胀,或者是暴力破解,但是验证码做的难以破解是比较困难的,因为现在机器识别
功能是灰常强大的;要做一个比较难以被机器识别的验证码应该要扭曲旋转,连体,多色彩,干扰线,不要说什么来个微积分什么的,那是给普通人用的玩意吗?我们要追求
大众化,所以还是按照传统的标准来搞
我们看到网上都流传很多验证码框架,例如jcaptcha,kcaptcha等等.下面我展示下如何使用jcaptcha实现一个自定义的验证码,首先用过jcaptcha的人都知道他的验证码内容
是不支持放到cookie里面,而是放到本地的mapstore的fastmap里面,所以很明显是不支持集群的,所以下面也看看我是如何扩展
先扩展一个验证码的样式类
package com.silvery.plugin.validcode.jcaptcha; import java.awt.Font; import java.awt.image.ImageFilter; import com.jhlabs.image.WaterFilter; import com.octo.captcha.component.image.backgroundgenerator.AbstractBackgroundGenerator; import com.octo.captcha.component.image.backgroundgenerator.FunkyBackgroundGenerator; import com.octo.captcha.component.image.color.RandomRangeColorGenerator; import com.octo.captcha.component.image.deformation.ImageDeformation; import com.octo.captcha.component.image.deformation.ImageDeformationByFilters; import com.octo.captcha.component.image.fontgenerator.FontGenerator; import com.octo.captcha.component.image.fontgenerator.RandomFontGenerator; import com.octo.captcha.component.image.textpaster.DecoratedRandomTextPaster; import com.octo.captcha.component.image.textpaster.TextPaster; import com.octo.captcha.component.image.textpaster.textdecorator.LineTextDecorator; import com.octo.captcha.component.image.textpaster.textdecorator.TextDecorator; import com.octo.captcha.component.image.wordtoimage.DeformedComposedWordToImage; import com.octo.captcha.component.image.wordtoimage.WordToImage; import com.octo.captcha.component.word.wordgenerator.RandomWordGenerator; import com.octo.captcha.component.word.wordgenerator.WordGenerator; import com.octo.captcha.engine.image.ListImageCaptchaEngine; import com.octo.captcha.image.gimpy.GimpyFactory; /** * 自定义验证码内容样式(通过源码改编) * * @author shadow * @email 124010356@qq.com * @create 2012.04.28 */ public class SimpleCaptchaEngine extends ListImageCaptchaEngine { /** * this method should be implemented as folow : * <ul> * <li>First construct all the factories you want to initialize the gimpy * with</li> * <li>then call the this.addFactoriy method for each factory</li> * </ul> */ protected void buildInitialFactories() { // build filters 波浪实现类 WaterFilter water = new WaterFilter(); water.setAmplitude(3d); water.setAntialias(true); water.setPhase(30d); water.setWavelength(80d); ImageDeformation backDef = new ImageDeformationByFilters(new ImageFilter[] {}); ImageDeformation textDef = new ImageDeformationByFilters(new ImageFilter[] {}); ImageDeformation postDef = new ImageDeformationByFilters(new ImageFilter[] { water }); // word generator WordGenerator dictionnaryWords = new RandomWordGenerator("abcdefhjkmnprstuvwxyz23456789"); // wordtoimage components int[] R = new int[] { 0, 120 }; int[] G = new int[] { 0, 120 }; int[] B = new int[] { 0, 120 }; RandomRangeColorGenerator colors = new RandomRangeColorGenerator(R, G, B); // Arial,Tahoma,Verdana,Helvetica,宋体,黑体,幼圆, 字体大小 Font[] fonts = new Font[] { new Font("Arial", 0, 10), new Font("Tahoma", 0, 10), new Font("Verdana", 0, 10), new Font("Helvetica", 0, 10), new Font("宋体", 0, 10), new Font("黑体", 0, 10), new Font("幼圆", 0, 10) }; // 设置字符以及干扰线颜色 RandomRangeColorGenerator lineColors = new RandomRangeColorGenerator(R, G, B); // 添加干扰线(可选取圆点干扰实现类BaffleTextDecorator LineTextDecorator) TextPaster randomPaster = new DecoratedRandomTextPaster(4, 4, colors, true, new TextDecorator[] { new LineTextDecorator(1, lineColors) }); RandomRangeColorGenerator backColorGenerator = new RandomRangeColorGenerator(new int[] { 75, 255 }, new int[] { 75, 255 }, new int[] { 75, 255 }); // 背景描绘 AbstractBackgroundGenerator back = new FunkyBackgroundGenerator(140, 50, backColorGenerator); FontGenerator shearedFont = new RandomFontGenerator(35, 0, fonts); // word2image 1 WordToImage word2image = new DeformedComposedWordToImage(shearedFont, back, randomPaster, backDef, textDef, postDef); // 输入图片 this.addFactory(new GimpyFactory(dictionnaryWords, word2image)); } }
再写一个存放验证码类的实现类,简单的来说就是自己实现一个CaptchaStore接口的类,我们的验证码内容都是从这里读写
package com.silvery.plugin.validcode.jcaptcha; import java.util.Collection; import java.util.Locale; import net.rubyeye.xmemcached.MemcachedClient; import org.apache.commons.collections.CollectionUtils; import com.octo.captcha.Captcha; import com.octo.captcha.service.CaptchaServiceException; import com.octo.captcha.service.captchastore.CaptchaAndLocale; import com.octo.captcha.service.captchastore.CaptchaStore; import com.silvery.core.spring.context.SpringHolder; // Referenced classes of package com.octo.captcha.service.captchastore: // CaptchaAndLocale, CaptchaStore public class MemcacheCaptchaStore implements CaptchaStore { private Object getCaptchaById(String id) { try { return getClient().get(id); } catch (Exception e) { e.printStackTrace(); return null; } } private MemcachedClient getClient() { Object obj = SpringHolder.getBean(MemcachedClient.class); if (obj == null) { throw new RuntimeException("Not found memcache instance"); } return (MemcachedClient) obj; } public boolean hasCaptcha(String id) { try { if (getCaptchaById(id) != null) { return true; } } catch (Exception e) { e.printStackTrace(); } return false; } public void storeCaptcha(String id, Captcha captcha) throws CaptchaServiceException { try { getClient().set(id, 1800, new CaptchaAndLocale(captcha)); } catch (Exception e) { e.printStackTrace(); } } public void storeCaptcha(String id, Captcha captcha, Locale locale) throws CaptchaServiceException { try { getClient().set(id, 1800, new CaptchaAndLocale(captcha, locale)); } catch (Exception e) { throw new CaptchaServiceException(e); } } public Captcha getCaptcha(String id) throws CaptchaServiceException { try { Object captchaAndLocale = getCaptchaById(id); return captchaAndLocale == null ? null : ((CaptchaAndLocale) captchaAndLocale).getCaptcha(); } catch (Exception e) { throw new CaptchaServiceException(e); } } public Locale getLocale(String id) throws CaptchaServiceException { try { Object captchaAndLocale = getCaptchaById(id); return captchaAndLocale == null ? null : ((CaptchaAndLocale) captchaAndLocale).getLocale(); } catch (Exception e) { throw new CaptchaServiceException(e); } } public boolean removeCaptcha(String id) { try { if (getCaptcha(id) != null) { return getClient().delete(id); } } catch (Exception e) { e.printStackTrace(); } return false; } public int getSize() { return 0; } public Collection<?> getKeys() { return CollectionUtils.EMPTY_COLLECTION; } public void empty() { } public void initAndStart() { } public void cleanAndShutdown() { } }
基本的扩展类都搞好了,我们该在哪里传入给框架使用呢?请看下面的单例使用
package com.silvery.plugin.validcode.jcaptcha; import com.octo.captcha.service.image.DefaultManageableImageCaptchaService; import com.octo.captcha.service.image.ImageCaptchaService; /** * * @author shadow * @email 124010356@qq.com * @create 2012.04.28 */ public class SimpleCaptchaService { private SimpleCaptchaService() { // nothing } private static ImageCaptchaService instance = null; /** * SimpleListSoundCaptchaEngine //还可以用声音 SpellerSoundCaptchaEngine * SpellerSoundCaptchaEngine DefaultGimpyEngineCaptcha * BaffleListGimpyEngineCaptcha BasicListGimpyEngineCaptcha * DeformedBaffleListGimpyEngineCaptcha DoubleRandomListGimpyEngineCaptcha * SimpleListImageCaptchaEngineCaptcha SimpleFishEyeEngineCaptcha */ static { instance = new DefaultManageableImageCaptchaService(new MemcacheCaptchaStore(), new SimpleCaptchaEngine(), 180, 100000, 75000); } public static ImageCaptchaService getInstance() { return instance; } public static boolean validate(String s, Object input) { return instance.validateResponseForID(s, input); } }
写个servlet让我们的页面可以访问生成出验证码图片
package com.silvery.plugin.validcode.jcaptcha; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.UUID; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.octo.captcha.service.CaptchaServiceException; import com.silvery.security.variable.Const; import com.silvery.utils.CookieUtils; import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGImageEncoder; /** * jcaptcha图片生成 * * @author shadow * @email 124010356@qq.com * @create 2012.04.28 */ @SuppressWarnings("serial") public class CaptchaServlet extends HttpServlet { public void init(ServletConfig servletConfig) throws ServletException { super.init(servletConfig); } protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException { byte[] captchaChallengeAsJpeg = null; // the output stream to render the captcha image as jpeg into ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream(); try { // get the session id that will identify the generated captcha. // the same id must be used to validate the response, the session id // is a good candidate! String captchaId = UUID.randomUUID().toString(); // call the ImageCaptchaService getChallenge method BufferedImage challenge = SimpleCaptchaService.getInstance().getImageChallengeForID(captchaId, httpServletRequest.getLocale()); // a jpeg encoder JPEGImageEncoder jpegEncoder = JPEGCodec.createJPEGEncoder(jpegOutputStream); jpegEncoder.encode(challenge); httpServletResponse.addCookie(CookieUtils.createCookie(Const.USER_MODULE_JCAPTCHA, captchaId, 1800)); } catch (IllegalArgumentException e) { httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND); return; } catch (CaptchaServiceException e) { httpServletResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return; } captchaChallengeAsJpeg = jpegOutputStream.toByteArray(); // flush it in the response 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(); } }
最后我们配置下web.xml启动项目看看效果咯
<servlet> <servlet-name>jcaptcha</servlet-name> <servlet-class>com.silvery.plugin.validcode.jcaptcha.CaptchaServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>jcaptcha</servlet-name> <url-pattern>*.jcapt</url-pattern> </servlet-mapping>