kaptcha生成自定义图片验证码

因为项目需要,在发短信验证码 之前加上图片验证码。根据网上提供的资料很快的就实现了。因为kaptcha相当于是别人封装的,效果比较单一,所以在简单实现后自己试着改了改。虽然还是挺不好看的,还是想自己记录下来,下次就可以看自己的笔记而不用再在网上找了。

先写最简单的配置实现的吧,效果图:


1.maven项目首先配置pom文件导入jar包;

		<dependency>  
		    <groupId>com.google.code.kaptcha</groupId>  
		    <artifactId>kaptcha</artifactId>  
		    <version>2.3</version>  
		</dependency>  

2..spring 配置文件配置

<!-- 配置kaptcha验证码 -->  
	<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.image.width">100</prop>  
			<!-- 图片高度 -->
	                    <prop key="kaptcha.image.height">50</prop>  
			 <!-- 图片干扰线:放开注释不要干扰线 -->
	                   <!--  <prop key="kaptcha.noise.impl">com.google.code.kaptcha.impl.NoNoise</prop>   -->
			<!-- 验证码字符串 -->     
	                    <prop key="kaptcha.textproducer.char.string">0123456789</prop>  
			<!-- 验证码长度 -->
	                    <prop key="kaptcha.textproducer.char.length">6</prop>  
	                </props>  
	            </constructor-arg>  
	        </bean>  
	    </property>  
	</bean> 


3.控制层:这段代码基本 上都大同小异。我是将验证码存入redis。

@RequestMapping("/kaptcha")  
    public SingtonResult<byte[]> handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception{  
        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("image/jpeg");  
        String token = (String) request.getAttribute("token");
        String capText = captchaProducer.createText();  
        //redis存值
        String  redisKey= "PC-kaptcha:"+token;
		rc.SetObjectByKey(redisKey, capText);
        BufferedImage bi = captchaProducer.createImage(capText);  
        ServletOutputStream out = response.getOutputStream();  
        ImageIO.write(bi, "jpg", out);  
    
        try {  
        	out.flush();  
        } finally {  
        	out.close();  
        }  
        return null;//SingtonResult.getSingtonSuccessInstance(b,"成功");
    } 

4.前端页面:

<img class="img-code" title="换一换" alt="加載中" src="/services/user/kaptcha.do" οnclick="this.src="/services/user/kaptcha.do?v="+Math.random()">

哦了,简单的很丑的图片验证码就好了。有没有发现在页面可能会格格不入,现在就需要自己去优化了。我没事的时候写了一下。

1.修改spring 配置如下:

<bean id="captchaProducer" class="com.qfy.common.utils.Kaptcha">  
	    <property name="config">  
	        <bean class="com.google.code.kaptcha.util.Config">  
	            <constructor-arg type="java.util.Properties">  
	                <props>  
	                	<!-- 图片宽度 -->
	                    <prop key="kaptcha.image.width">117</prop>  
	                    <!-- 图片高度 -->
	                    <prop key="kaptcha.image.height">40</prop>  
	                    <!-- 图片干扰线实现类 -->
	                     <prop key="kaptcha.noise.impl">com.qfy.common.utils.Noise</prop>   
	                     <!-- 验证码字符串 -->
	                    <prop key="kaptcha.textproducer.char.string">0123456789</prop>  
	                    <!-- 验证码长度 -->
	                    <prop key="kaptcha.textproducer.char.length">6</prop>  
	                    <!-- 字体大小,默认40 -->
	                    <prop key="kaptcha.textproducer.font.size">27</prop>
	                    <!-- 字体样式 -->
	                    <prop key="kaptcha.textproducer.font.names">微软雅黑</prop>
	                    
	                    <prop key="kaptcha.background.clear.to">white</prop>
	                    <!-- 是否需要边框:yes,no;默认yes -->
	                    <prop key="kaptcha.border">no</prop>
	                    <!-- 边框粗细:默认1 -->
	                  <!--   <prop key="kaptcha.border.thickness">1</prop> -->
	                </props>  
	            </constructor-arg>  
	        </bean>  
	    </property>  
	</bean>  
可以看到我自定义了干扰线类实现NoiseProducer
com.qfy.common.utils.Noise

package com.qfy.common.utils;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.util.Random;

import com.google.code.kaptcha.NoiseProducer;
import com.google.code.kaptcha.util.Configurable;

/**
 * 图片验证码干扰线实现类
 * @author ptt
 *
 */
public class Noise extends Configurable implements NoiseProducer
{
	/**
	 * Draws a noise on the image. The noise curve depends on the factor values.
	 * Noise won't be visible if all factors have the value > 1.0f
	 * 
	 * @param image
	 *            the image to add the noise to
	 * @param factorOne
	 * @param factorTwo
	 * @param factorThree
	 * @param factorFour
	 */
	public void makeNoise(BufferedImage image, float factorOne,
			float factorTwo, float factorThree, float factorFour)
	{
		// image size
		int width = image.getWidth();
		int height = image.getHeight();

		// the points where the line changes the stroke and direction
		Point2D[] pts = null;
		Random rand = new Random();

		//   使用 float 坐标指定的三次参数曲线段。
		CubicCurve2D cc = new CubicCurve2D.Float(width * factorOne, height
				* rand.nextFloat(), width * factorTwo, height
				* rand.nextFloat(), width * factorThree, height
				* rand.nextFloat(), width * factorFour, height
				* rand.nextFloat());

		//  返回定义变平形状边界的迭代对象。
		PathIterator pi = cc.getPathIterator(null,2);
		Point2D tmp[] = new Point2D[200];
		int i = 0;

		// while pi is iterating the curve, adds points to tmp array
		while (!pi.isDone())
		{
			float[] coords = new float[6];
			switch (pi.currentSegment(coords))//使用迭代返回当前路径段的坐标和类型,必须传入长度为 6 的 float 数组
			{
				case PathIterator.SEG_MOVETO: //SEG_MOVETO 和 SEG_LINETO 类型返回一个点
				case PathIterator.SEG_LINETO:
				case PathIterator.SEG_CUBICTO: //SEG_CUBICTO 返回 3 个点
				case PathIterator.SEG_QUADTO: //SEG_QUADTO 返回两个点
					tmp[i] = new Point2D.Float(coords[0], coords[1]);
			}
			i++;
			pi.next();
		}

		pts = new Point2D[i];
		System.arraycopy(tmp, 0, pts, 0, i);

		Graphics2D graph = (Graphics2D) image.getGraphics();
		graph.setRenderingHints(new RenderingHints(
				RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON));


		// 设置干扰线10条;可以自行设置,每条干扰线的颜色位置都随机产生
		for (i = 0; i < 10; i++)
		{
			int xs = rand.nextInt(width);  
			int ys = rand.nextInt(height);  
			int xe = xs+rand.nextInt(width/8);  
			int ye = ys+rand.nextInt(height/8);  
			graph.setColor(getColor());  
			graph.drawLine(xs, ys, xe, ye);  
		}

		graph.dispose();
	}
	/**
	 * 干扰线 的颜色随机生成
	* @Title: getColor 
	*@author :彭婷婷
	* @Description: TODO
	* @param @return   
	* @return Color    
	* @throws 
	* @data  2017年3月31日下午1:32:32
	 */
	private Color getColor(){
		Random random = new Random();
		int red = random.nextInt(255);  
		int green = random.nextInt(255);  
		int blue = random.nextInt(255);  
		return new Color(red, green, blue);  
	}
}

试试现在的验证码就是这个样了。

还是有点 丑,将就吧,继续,干扰线五颜六色了,接下来就是字符串的色彩,字体改变了。当然,还是得在原有基础改写。

先修改spring配置文件

<bean id="captchaProducer" class="com.qfy.common.utils.Kaptcha">  

自己重写DefaultKaptcha类,实现Producer。贴代码

package com.qfy.common.utils;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import java.util.Random;

import com.google.code.kaptcha.BackgroundProducer;
import com.google.code.kaptcha.GimpyEngine;
import com.google.code.kaptcha.Producer;
import com.google.code.kaptcha.text.TextProducer;
import com.google.code.kaptcha.text.WordRenderer;
import com.google.code.kaptcha.util.Configurable;

/**
 * Default {@link Producer} implementation which draws a captcha image using
 * {@link WordRenderer}, {@link GimpyEngine}, {@link BackgroundProducer}.
 * Text creation uses {@link TextProducer}.
 */
public class Kaptcha extends Configurable implements Producer
{
	private int width = 200;

	private int height = 50;

	/**
	 * Create an image which will have written a distorted text.
	 * 
	 * @param text
	 *            the distorted characters
	 * @return image with the text
	 */
	public BufferedImage createImage(String text)
	{
		GimpyEngine gimpyEngine = getConfig().getObscurificatorImpl();
		BackgroundProducer backgroundProducer = getConfig().getBackgroundImpl();
		boolean isBorderDrawn = getConfig().isBorderDrawn();
		this.width = getConfig().getWidth();
		this.height = getConfig().getHeight();

		BufferedImage bi = renderWord(text, width, height);
		bi = gimpyEngine.getDistortedImage(bi);
		bi = backgroundProducer.addBackground(bi);
		Graphics2D graphics = bi.createGraphics();
		if (isBorderDrawn)
		{
			drawBox(graphics);
		}
		return bi;
	}

	private void drawBox(Graphics2D graphics)
	{
		Color borderColor = getConfig().getBorderColor();
		int borderThickness = getConfig().getBorderThickness();

		graphics.setColor(borderColor);

		if (borderThickness != 1)
		{
			BasicStroke stroke = new BasicStroke((float) borderThickness);
			graphics.setStroke(stroke);
		}

		Line2D line1 = new Line2D.Double(0, 0, 0, width);
		graphics.draw(line1);
		Line2D line2 = new Line2D.Double(0, 0, width, 0);
		graphics.draw(line2);
		line2 = new Line2D.Double(0, height - 1, width, height - 1);
		graphics.draw(line2);
		line2 = new Line2D.Double(width - 1, height - 1, width - 1, 0);
		graphics.draw(line2);
	}

	/**
	 * @return the text to be drawn
	 */
	public String createText()
	{
		return getConfig().getTextProducerImpl().getText();
	}
	
	public BufferedImage renderWord(String word, int width, int height)
	{
		int fontSize = getConfig().getTextProducerFontSize();
		Font[] fonts = getConfig().getTextProducerFonts(fontSize);
		BufferedImage image = new BufferedImage(width, height,
				BufferedImage.TYPE_INT_ARGB);
		Graphics2D g2D = image.createGraphics();

		RenderingHints hints = new RenderingHints(
				RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		hints.add(new RenderingHints(RenderingHints.KEY_RENDERING,
				RenderingHints.VALUE_RENDER_QUALITY));
		g2D.setRenderingHints(hints);

		FontRenderContext frc = g2D.getFontRenderContext();
		Random random = new Random();

		int startPosY = (height - fontSize) / 5 + fontSize;

		char[] wordChars = word.toCharArray();
		Font[] chosenFonts = new Font[wordChars.length];
		int [] charWidths = new int[wordChars.length];
		int widthNeeded = 0;
		for (int i = 0; i < wordChars.length; i++)
		{
			chosenFonts[i] = fonts[random.nextInt(fonts.length)];
			char[] charToDraw = new char[]{
				wordChars[i]
			};
			GlyphVector gv = chosenFonts[i].createGlyphVector(frc, charToDraw);
			charWidths[i] = (int)gv.getVisualBounds().getWidth();
			if (i > 0)
			{
				widthNeeded = widthNeeded + 2;
			}
			widthNeeded = widthNeeded + charWidths[i];
		}
		
		int startPosX = (width - widthNeeded) / 2;
		for (int i = 0; i < wordChars.length; i++)
		{
			g2D.setColor(getColor());
			g2D.setFont(chosenFonts[i]);
			char[] charToDraw = new char[] {
				wordChars[i]
			};
			g2D.drawChars(charToDraw, 0, charToDraw.length, startPosX, startPosY);
			startPosX = startPosX + (int) charWidths[i] ;
		}

		return image;
	}
	
	private Color getColor(){
		Random random = new Random();
		int red = random.nextInt(255);  
		int green = random.nextInt(255);  
		int blue = random.nextInt(255);  
		return new Color(red, green, blue);  
	}
}


看看效果,贴图;


就这样就可以实现自己想要的效果了,不再是默认的黑白色了。效果就自己调节到满意为止就行了。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值