Jpatchca生成验证码感觉不好使,对图的参数控制不好,可能导致图很高,但是文字却根本没占满,如果文字设置大了,会抛出异常,说文字太高了。
其二,Jpatchca不支持集群环境,默认的验证码不是保存在session中,如果想做个性化的处理很麻烦。其实我想要的就是一个声称图片的流就ok了,剩下的事情就交给程序员自己实现吧。
最终,选择了patchca(另一个开源的组件)来实现,这个验证码是放到session中的,也可以自己指定。很灵活。
这里对Jpatchca做个备忘:
<dependency> <groupId>com.octo.captcha</groupId> <artifactId>jcaptcha</artifactId> <version>2.0-alpha-1</version> </dependency> <dependency> <groupId>com.octo.captcha</groupId> <artifactId>jcaptcha-integration-simple-servlet</artifactId> <version>2.0-alpha-1</version> <exclusions> <exclusion> <artifactId>servlet-api</artifactId> <groupId>javax.servlet</groupId> </exclusion> </exclusions> </dependency>
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <display-name>Lrtech_framework</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:framework/spring-front.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:/spring-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
<?xml version="1.0" encoding="UTF-8"?> <!--suppress XmlUnboundNsPrefix --> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <mvc:annotation-driven/> <context:annotation-config/> <context:component-scan base-package="com.lavasoft.ntv.web"/> <mvc:resources mapping="/backui/**" location="/backui/"/> <mvc:resources mapping="/frontui/**" location="/frontui/"/> <bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean"> <property name="favorPathExtension" value="true" /> <property name="favorParameter" value="true" /> <property name="parameterName" value="format" /> <property name="ignoreAcceptHeader" value="true" /> <property name="mediaTypes"> <value> json=application/json xml=application/xml html=text/html </value> </property> <property name="defaultContentType" value="text/html" /> </bean> <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"> <property name="order" value="1" /> <property name="contentNegotiationManager" ref="contentNegotiationManager"/> <property name="defaultViews"> <list> <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"> <property name="prettyPrint" value="true"/> <property name="extractValueFromSingleKeyModel" value="true"/> </bean> </list> </property> </bean> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="contentType" value="text/html;charset=UTF-8" /> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="prefix" value="/pages/"/> <property name="suffix" value=".jsp"/> </bean> </beans>
<?xml version="1.0" encoding="UTF-8"?> <beans default-autowire="byName" xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" xmlns:context="http://www.springframework.org/schema/context"> <import resource="classpath:/framework/spring-back.xml"/> <bean id="fastHashMapCaptchaStore" class="com.octo.captcha.service.captchastore.FastHashMapCaptchaStore"/> <bean id="captchaEngineEx" class="com.lavasoft.ntv.web.captchea.MyCaptchaEngine"/> <bean id="imageCaptchaService" class="com.octo.captcha.service.image.DefaultManageableImageCaptchaService"> <constructor-arg type="com.octo.captcha.service.captchastore.CaptchaStore" index="0"> <ref bean="fastHashMapCaptchaStore"/> </constructor-arg> <constructor-arg type="com.octo.captcha.engine.CaptchaEngine" index="1"> <ref bean="captchaEngineEx"/> </constructor-arg> <constructor-arg index="2"> <value>180</value> </constructor-arg> <constructor-arg index="3"> <value>100000</value> </constructor-arg> <constructor-arg index="4"> <value>75000</value> </constructor-arg> </bean> </beans>
package com.lavasoft.ntv.web.captchea;
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.DecoratedRandomTextPaster;
import com.octo.captcha.component.image.textpaster.TextPaster;
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;
import java.awt.*;
import java.awt.image.ImageFilter;
/**
* 生成验证码引擎实现
*
* @author leizhimin 14-5-4 上午10:55
*/
public class MyCaptchaEngine extends ListImageCaptchaEngine {
protected void buildInitialFactories() {
int minWordLength = 4;
int maxWordLength = 4;
int fontSize = 16;
int imageWidth = 120;
int imageHeight = 28;
WordGenerator wordGenerator = new RandomWordGenerator("23456789abcdefghjkmnpqrstuvwxyz");
TextPaster randomPaster = new DecoratedRandomTextPaster(minWordLength,
maxWordLength, new RandomListColorGenerator(new Color[]{
new Color(23, 170, 27), new Color(220, 34, 11),
new Color(23, 67, 172)}), new TextDecorator[]{});
BackgroundGenerator background = new UniColorBackgroundGenerator(imageWidth, imageHeight, Color.white);
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)});
ImageDeformation postDef = new ImageDeformationByFilters(new ImageFilter[]{});
ImageDeformation backDef = new ImageDeformationByFilters(new ImageFilter[]{});
ImageDeformation textDef = new ImageDeformationByFilters(new ImageFilter[]{});
WordToImage word2image = new DeformedComposedWordToImage(font,
background, randomPaster, backDef, textDef, postDef);
addFactory(new GimpyFactory(wordGenerator, word2image));
}
}
package com.lavasoft.ntv.web;
import com.octo.captcha.service.CaptchaServiceException;
import com.octo.captcha.service.image.ImageCaptchaService;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
* 登录逻辑
*
* @author leizhimin 14-5-4 上午11:00
*/
@Controller
public class LoginController {
@Resource(name = "imageCaptchaService")
private ImageCaptchaService imageCaptchaService;
/**
* 生成验证码图片io流
*/
@RequestMapping(value = "/generateImage")
public void ImageCaptcha(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("------------------- /generateImage");
byte[] captchaChallengeAsJpeg = null;
ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
try {
String sessionid = request.getSession().getId();
BufferedImage challenge = imageCaptchaService.getImageChallengeForID(sessionid, request.getLocale());
JPEGImageEncoder jpegEncoder = JPEGCodec.createJPEGEncoder(jpegOutputStream);
jpegEncoder.encode(challenge);
} catch (Exception e) {
e.printStackTrace();
}
captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
response.setHeader("Cache-Control", "no-store");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/jpeg");
ServletOutputStream responseOutputStream = response.getOutputStream();
responseOutputStream.write(captchaChallengeAsJpeg);
responseOutputStream.flush();
responseOutputStream.close();
}
/**
* 验证验证码,并进行登录
*/
@RequestMapping("/login")
private String LoginAction(HttpServletRequest request,
HttpServletResponse response,
String username,
String password,
String yzm) {
boolean flag = false;
String sessionid = request.getSession().getId();
String captcha_value = request.getParameter("yzm");
System.out.println("反馈的验证码:" + captcha_value);
try {
flag = imageCaptchaService.validateResponseForID(sessionid, captcha_value);
} catch (CaptchaServiceException e) {
}
System.out.println("验证结果为:" + flag + ",sessionID=" + sessionid + ",验证码=" + captcha_value);
if (flag) {
//todo:验证用户名和密码逻辑,
}
return flag ? "good" : "error";
}
@RequestMapping("/hello")
private String hello() {
System.out.println("-------hello!------");
return "hello";
}
@RequestMapping("/mp")
private ModelMap mp() {
System.out.println("-------ModelMap!------");
ModelMap map = new ModelMap();
map.put("as", "sadfas");
map.put("as1", "sadfas");
map.put("as2", "sadfas");
return map;
}
@RequestMapping("/mp2")
private ModelAndView mp2() {
System.out.println("-------ModelMap!------");
ModelAndView mv = new ModelAndView();
ModelMap map = mv.getModelMap();
map.put("as1", "sadfas1");
map.put("as2", "sadfas2");
mv.setViewName("mp2");
return mv;
}
}
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<link href="../backui/ligerUI/skins/Aqua/css/ligerui-all.css" rel="stylesheet" type="text/css"/>
<link href="../backui/ligerUI/skins/Tab/css/form.css" rel="stylesheet" type="text/css"/>
<script src="../backui/jquery/jquery-1.4.4.min.js" type="text/javascript"></script>
<script src="../backui/ligerUI/js/ligerui.all.js" type="text/javascript"></script>
<script src="../backui/jquery-validation/jquery.validate.min.js" type="text/javascript"></script>
<script src="../backui/jquery-validation/jquery.metadata.js" type="text/javascript"></script>
<script src="../backui/jquery-validation/messages_cn.js" type="text/javascript"></script>
<style type="text/css">
input.error {
border: 1px dotted red;
}
label.error {
background-image: url('../backui/jquery-validation/pic/error.png');
background-repeat: no-repeat;
padding-left: 18px;
color: red;
}
.align-center1{
margin:0 auto; /* 居中 这个是必须的,,其它的属性非必须 */
width: 400px;
background-color: #9AC6FF;
text-align:left; /* 文字等内容居中 */
}
</style>
<script>
function chanage(srcObj){
srcObj.src="/ntv/generateImage?"+Math.random();
}
$().ready(function () {
$.metadata.setType("attr", "validate");
$("#bt_login"). alert("bt_login");
})
$("#signupForm").validate();
});
</script>
</head>
<body>
<div class="align-center1">sdfasdf</div>
<div class="align-center1">
<form id="signupForm" method="get" action="/ntv/login">
<p>
<label for="firstname">username</label>
<input id="firstname" name="firstname" validate="{required:true}"/>
<p>
<p>
<label for="password">password</label>
<input id="password" name="password" type="password" validate="{required:true,minlength:1}"/>
</p>
<p>
<label for="confirm_password">确认密码</label>
<input id="confirm_password" name="confirm_password" type="password"
validate="{equalTo:'#password'}"/>
</p>
<p>
<label for="confirm_password">验证码</label>
<input id="yzm" name="yzm" type="text"validate="{required:true"/>
<img id="img_yzm" src="/ntv/generateImage" </p>
<p>
<%--<input class="submit" type="submit" value="登录"/>--%>
<input type="button" id="bt_login" name="bt_login" value="登录"/>
</p>
</form>
</div>
</body>
</html>