shiro安全框架扩展教程--验证码的安全(jcaptcha框架)

  1. 云栖社区>
  2. 博客>
  3. 正文

shiro安全框架扩展教程--验证码的安全(jcaptcha框架)

aielves 2014-11-02 20:08:08 浏览634
展开阅读全文

        我们看到很多网站上都有验证码的功能,因为他们需要防止机器人注册等等,导致数据膨胀,或者是暴力破解,但是验证码做的难以破解是比较困难的,因为现在机器识别

功能是灰常强大的;要做一个比较难以被机器识别的验证码应该要扭曲旋转,连体,多色彩,干扰线,不要说什么来个微积分什么的,那是给普通人用的玩意吗?我们要追求

大众化,所以还是按照传统的标准来搞


我们看到网上都流传很多验证码框架,例如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>





网友评论

登录后评论
0/500
评论
aielves
+ 关注