`
aasonwu
  • 浏览: 34585 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Struts2整合Jcaptcha1.0实现异步验证码

阅读更多

验证码的英文CAPTCHA 这个词最早是在2000年由卡内基梅隆大学的Luis von Ahn、Manuel Blum、Nicholas J.Hopper以及IBM的John Langford所提出。CAPTCHA 是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试)的缩写,是一种区分用户是计算机和人的公共全自动程序。这个问题可以由计算机生成并评判,但是必须只有人类才能解答。由于计算机无法解答CAPTCHA的问题,所以回答出问题的用户就可以被认为是人类。

----引自"百度百科"

首先,从sourceforge网站下载Download jcaptcha-1.0-bin.zip (9.2 MB)

jCaptcha官方网站是https://jcaptcha.atlassian.net/wiki/display/general/Home

如果机上装有SVN客户端,还可以下载源代码,SVN地址是:

https://jcaptcha.svn.sourceforge.net/svnroot/jcaptcha

以上地址从SVN上checkout出整个jcaptcha项目(包括trunk,tags,branch等)100多M,网速慢的比较耗时,建议先浏览仓库,checkout一个tags标签版本即可

当然,如果机上又装mvn,那就可以打包成源代码的jar文件了,方便我们学习,mvn命令是: mvn source:jar


确定环境中包含下列依赖:


其中 jcaptcha依赖于 commons-logging和commons-collections

集成struts2依赖于jcaptcha4struts2

strut2-sitemesh和sitemesh已本博文无关


1,查看jcaptcha4struts2插件配置struts-plugin.xml

<struts>


	<include file="struts-default.xml" />

	<package name="jcaptcha4struts2-default" extends="struts-default">

		<result-types>
			<result-type name="captchaImage"
				class="com.google.code.jcaptcha4struts2.core.actions.support.CaptchaImageResult" />
		</result-types>

		<interceptors>
			<interceptor name="jcaptchaValidation"
				class="com.google.code.jcaptcha4struts2.core.interceptors.JCaptchaValidationIntercepter" />
			<interceptor-stack name="jcaptchaDefaultStack">
				<interceptor-ref name="defaultStack" />
				<interceptor-ref name="jcaptchaValidation" />
			</interceptor-stack>
		</interceptors>

		<action name="jcaptcha_image"
			class="com.google.code.jcaptcha4struts2.core.actions.JCaptchaImageAction">
			<result type="captchaImage" />
		</action>

	</package>

</struts>


2.单纯使用内置的验证码功能

如果单纯使用jcaptcha4struts2插件提供的验证功能,仅需在页面包含一个<img src=''jcaptcha_image.action" />图片元素就能拿到验证码图片了
而提交表单的时候,要对验证码进行验证,仅需在表单的action中添加jcaptchaValidation拦截器即可

例:index.jsp

[...]
<script type="text/javascript">
	function changeImage() {
		var jcaptcha_image = document.getElementById("jcaptcha_image");
		var timestamp = new Date().getTime();
		jcaptcha_image.src = "ajax_captcha.action?timestamp=" + timestamp;
		var jcaptcha_input = document.getElementById("txtVerifyCode");
		jcaptcha_input.value = "";
		jcaptcha_input.focus();
	}
</script>
</head>

<body>
	<div class="form">
		<form id="register" name="register" method="post"
			action="user/register">
			[...]
			<table width="800px" style="border-collapse:collapse">
				[...]
				<tr>
					<td class="label"><label for="verify_code">验证码:</label>
					</td>
					<td class="field">
						<div class="jcaptcha_image">
							<!-- 验证码 -->
							<img id="jcaptcha_image"
								src="jcaptcha_image.action" />
						</div>
						<!-- 验证码输入框 --> 
						<input name="jCaptchaResponse"
								type="text" id="txtVerifyCode" class="yzm_input clear-fix" />
						<div class="text_left">
							<p class="t1">
								<span id="vcodeValidMsg">请输入图片中的七个字母。</span>
								<span id="number.info" style="color:red"></span> 
								<a href="javascript:changeImage()">看不清楚?换个图片</a>
							</p>
						</div>
					</td>
				</tr>
				<tr>
					<td></td>
					<td class="btn-register"><button type="submit"
							id="btn_register" class="btn-register">同意协议并注册</button>
					</td>
				</tr>
			</table>
		</form>
	</div>
</body>
</html>

struts.xml配置文件添加拦截器

		<action name="register" class="org.tarena.dang.action.RegisterAction"
			method="register">
			<!-- 校验验证码拦截器 -->
			<interceptor-ref name="jcaptchaDefaultStack" />
			<result name="verify">/user/verify_form.jsp</result>
		</action>



效果如下:

3.自定义验证码

如果jcaptcha4struts2插件内置的验证码无法满足我们的需求,我们需要自己的验证码生成策略

  1. 首先,实现自己的CaptchaEngine
    package com.taobao.utils;
    
    import java.awt.Color;
    import java.awt.Font;
    import java.awt.image.ImageFilter;
    
    import com.jhlabs.image.WaterFilter;
    import com.octo.captcha.component.image.backgroundgenerator.BackgroundGenerator;
    import com.octo.captcha.component.image.backgroundgenerator.UniColorBackgroundGenerator;
    import com.octo.captcha.component.image.color.RandomListColorGenerator;
    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.MySimpleTextParser;
    import com.octo.captcha.component.image.textpaster.TextPaster;
    import com.octo.captcha.component.image.wordtoimage.DeformedComposedWordToImage;
    import com.octo.captcha.component.image.wordtoimage.WordToImage;
    import com.octo.captcha.component.word.FileDictionary;
    import com.octo.captcha.component.word.wordgenerator.ComposeDictionaryWordGenerator;
    import com.octo.captcha.component.word.wordgenerator.WordGenerator;
    import com.octo.captcha.engine.image.ListImageCaptchaEngine;
    import com.octo.captcha.image.gimpy.GimpyFactory;
    
    public class MySimpleWaterCaptchaEngine extends ListImageCaptchaEngine{
    
    	@Override
    	protected void buildInitialFactories() {
    
    		WaterFilter waterFilter = new WaterFilter();
    		waterFilter.setAmplitude(5d);//振幅
    		waterFilter.setAntialias(true);//锯齿或平滑
    		waterFilter.setPhase(-90);//相位
    		waterFilter.setWavelength(50d);//波长
    		
    		int minWordLength = 4; //最少字符数  
    		int maxWordLength = 5;  //最大字符数
    		int fontSize = 50; //字体大小
    		int imageWidth = 150;  //背景图长度
    		int imageHeight = 50;  //背景图宽度
    
    		//背景图生成
    		BackgroundGenerator backGround = new UniColorBackgroundGenerator(imageWidth, imageHeight, Color.white);
    		//生成单词
    		WordGenerator dictionnaryWords = new ComposeDictionaryWordGenerator(new FileDictionary("toddlist"));
    
    		//字体生成
    		FontGenerator font = new RandomFontGenerator(fontSize, fontSize, new Font[] {  
    				new Font("nyala", Font.BOLD, fontSize), new Font("Bell MT", Font.PLAIN, fontSize),  
    				new Font("Credit valley", Font.BOLD, fontSize) });  
    		//单词转化成图片
    		TextPaster randomPaster = new MySimpleTextParser(
    				minWordLength, 
    				maxWordLength, 
    				new RandomListColorGenerator(new Color[]{Color.black, Color.pink, Color.gray}),
    				true);
    		//backgroundDeformation 背景图变形
    		ImageDeformation backDef = new ImageDeformationByFilters(new ImageFilter[] {});  
    		//textDeformation 字符图层转变形
    		ImageDeformation textDef = new ImageDeformationByFilters(new ImageFilter[] {});  
    		//finalDeformation  最终图片变形
    		ImageDeformation postDef = new ImageDeformationByFilters(new ImageFilter[] {waterFilter});
    		//转化图片
    		WordToImage word2image = new DeformedComposedWordToImage(font, backGround, randomPaster, backDef, textDef,  
    				postDef);  
    		addFactory(new GimpyFactory(dictionnaryWords, word2image));  
    	}
    
    }
    

  2. 由于jcaptcha4struts2插件内置生成的图片文字是从背景的X坐标为background.getHeight()/20 , Y轴坐标为background.getWidth()/2开始画图的,所以看上去文字会走动比较大,我们可以自己修改一下,实现自己的验证码文字生成,由于AbstractTextPaster所在包访问权限有所限制,故我这里把实现放在与AbstractTextPaster同一个包内
    /*
     * JCaptcha, the open source java framework for captcha definition and integration
     * Copyright (c)  2007 jcaptcha.net. All Rights Reserved.
     * See the LICENSE.txt file distributed with this package.
     */
    
    
    package com.octo.captcha.component.image.textpaster;
    import com.octo.captcha.CaptchaException;
    import com.octo.captcha.component.image.color.ColorGenerator;
    
    import java.awt.*;
    import java.awt.image.BufferedImage;
    import java.text.AttributedString;
    
    /**
     * <p/>
     * Pastes the text at width/20 and height/2 </p>
     *
     * @author <a href="mailto:mag@jcaptcha.net">Marc-Antoine Garrigue </a>
     * @version 1.0
     */
    public class MySimpleTextParser extends AbstractTextPaster {
    
        public MySimpleTextParser(Integer minAcceptedWordLength, Integer maxAcceptedWordLength,
                                Color textColor) {
            super(minAcceptedWordLength, maxAcceptedWordLength, textColor);
        }
    
        public MySimpleTextParser(Integer minAcceptedWordLength, Integer maxAcceptedWordLength,
                                ColorGenerator colorGenerator) {
            super(minAcceptedWordLength, maxAcceptedWordLength, colorGenerator);
        }
    
        public MySimpleTextParser(Integer minAcceptedWordLength, Integer maxAcceptedWordLength,
                                ColorGenerator colorGenerator, Boolean manageColorPerGlyph) {
            super(minAcceptedWordLength, maxAcceptedWordLength, colorGenerator, manageColorPerGlyph);
        }
    
        /**
         * Pastes the attributed string on the backround image and return the final image. Implementation must take into
         * account the fact that the text must be readable by human and non by programs. Pastes the text at width/20 and
         * height/2
         *
         * @return the final image
         *
         * @throws com.octo.captcha.CaptchaException
         *          if any exception accurs during paste routine.
         */
        public BufferedImage pasteText(final BufferedImage background,
                                       final AttributedString attributedWord) throws CaptchaException {
            int x = (background.getWidth()) / 20;
            int bgWidth=background.getHeight();
            int y = bgWidth-(bgWidth/5);
            //创建一个与背景图片同尺寸的图层
            BufferedImage out = copyBackground(background);
            Graphics2D g2 = pasteBackgroundAndSetTextColor(out, background);
            //pie.drawString(attributedWord.getIterator(), x, y);
            //pie.dispose();
    
            // convert string into a series of glyphs we can work with
            ChangeableAttributedString newAttrString = new ChangeableAttributedString(g2,
                    attributedWord, 2);
    
            // space out the glyphs with a little kerning
            newAttrString.useMinimumSpacing(1);
            //newAttrString.useMonospacing(0);
            // shift string to a random spot in the output imge
            newAttrString.moveTo(x, y);
            // now draw each glyph at the appropriate spot on the image.
            if (isManageColorPerGlyph())
                newAttrString.drawString(g2, getColorGenerator());
            else
                newAttrString.drawString(g2);
    
            g2.dispose();
            return out;
        }
    }
    

  3. 继承jcaptcha4struts2的JCaptchaImageAction
    package com.taobao.action;
    
    import com.google.code.jcaptcha4struts2.core.actions.JCaptchaImageAction;
    import com.google.code.jcaptcha4struts2.core.beans.JC4S2Config;
    import com.octo.captcha.service.captchastore.FastHashMapCaptchaStore;
    import com.octo.captcha.service.image.DefaultManageableImageCaptchaService;
    import com.taobao.utils.MySimpleWaterCaptchaEngine;
    
    public class MyJcaptchaAction extends JCaptchaImageAction {
    
    	/**
    	 * 
    	 */
    	private static final long serialVersionUID = 1340725347539598783L;
    
    	private static MySimpleWaterCaptchaEngine imageCaptchaEngine = null;
    	private static FastHashMapCaptchaStore fastHashMapCaptchaStore = null;
    	private static DefaultManageableImageCaptchaService defaultManageableImageCaptchaService = null;
    	static {
    		imageCaptchaEngine=new MySimpleWaterCaptchaEngine();
    		fastHashMapCaptchaStore = new FastHashMapCaptchaStore();
    		defaultManageableImageCaptchaService=new DefaultManageableImageCaptchaService(fastHashMapCaptchaStore, imageCaptchaEngine, 180, 100000, 75000);
    		JC4S2Config.getInstance().setImageCaptchaService(defaultManageableImageCaptchaService);
    	}
    
    	@Override
    	public String execute(){
    		return super.execute();
    	}
    }
    

  4. 配置页面action
    	<package name="ajax-valid" extends="jcaptcha4struts2-default">
    		<action name="ajax_captcha" class="com.taobao.action.MyJcaptchaAction">
    			<interceptor-ref name="jcaptchaDefaultStack" />
    			<result type="captchaImage" />
    		</action>
    	</package>

    <tr>
    					<td class="label"><label for="verify_code">验证码:</label>
    					</td>
    					<td class="field">
    						<div class="jcaptcha_image">
    							<!-- 验证吗 -->
    							<img id="jcaptcha_image"
    								src="ajax_captcha.action" />
    						</div>
    						<!-- 验证码输入框 --> 
    						<input name="jCaptchaResponse"
    								type="text" id="txtVerifyCode" class="yzm_input clear-fix" />
    						<div class="text_left">
    							<p class="t1">
    								<span id="vcodeValidMsg">请输入图片中的七个字母。</span>
    								<span id="number.info" style="color:red"></span> 
    								<a href="javascript:changeImage()">看不清楚?换个图片</a>
    							</p>
    						</div>
    					</td>
    				</tr>

效果如下



分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics