验证码生成模块,配置信息基本和前面的模块一样。account-captcha需要提供的服务是生成随机的验证码主键,然后用户可以使用这个主键要求服务生成一个验证码图片,这个图片对应的值应该是随机的,最后用户用肉眼读取图片的值,并将验证码的主键与这个值交给服务进行验证。这一服务对应的接口可以定义如下:

具体代码:

public interface AccountCaptchaService {
//生成主见
String generateCaptchaKey() throws AccountCaptchaException;
byte[] generateCaptchaImage(String captchaKey) throws AccountCaptchaException;
//验证主键和值
boolean validateCaptcha(String captchaKey, String captchaValue)throws AccountCaptchaException;
List<String> getPreDefinedTexts();
void setPreDefinedTexts(List<String> preDefinedTexts);
}

  额外定义的getPreDefinedText和set方法可以预定义验证码图片的值,提高程序的可预测性。

为你了能够生成随机的验证码主键,定义一个类RandomGenerator如下:

public class RandomGenerator {

private static String rangeString = "0123456789qwertyuiopasdfghjklzxcvbnm";
public static synchronized String getRandomString(){
Random random = new Random();
StringBuffer result = new StringBuffer();
for (int i = 0; i < 8; i++) {
result.append(rangeString.charAt(random.nextInt(rangeString.length())));
}
return result.toString();
}
}

  该方法提供了一个线程安全且静态的方法,nextInt()会放回一个大于等于0且小于n的整数。

接口实现:

public class AccountCaptchaServiceImpl implements AccountCaptchaService,InitializingBean {

	private DefaultKaptcha producer;
private Map<String,String> captchaMap = new HashMap<String, String>();
private List<String> preDefinedTexts;
private int textCount = 0;
public void afterPropertiesSet() throws Exception {
producer = new DefaultKaptcha();
producer.setConfig(new Config(new Properties()));
} public String generateCaptchaKey() throws AccountCaptchaException {
String key = RandomGenerator.getRandomString();
String value = getCaptchaText();
captchaMap.put(key, value);
return key;
} private String getCaptchaText() {
if(preDefinedTexts != null && !preDefinedTexts.isEmpty()){
String text = preDefinedTexts.get(textCount);
textCount = (textCount+1)%preDefinedTexts.size();
return text;
}
else {
return producer.createText();
} } public byte[] generateCaptchaImage(String captchaKey)
throws AccountCaptchaException {
String text = captchaMap.get(captchaKey);
if (text == null) {
throw new AccountCaptchaException("captaha key"+captchaKey+"not found");
}
BufferedImage image = producer.createImage(text);
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
ImageIO.write(image,"jpg" , out);
} catch (Exception e) {
throw new AccountCaptchaException("failed to write image");
}
return out.toByteArray();
} public boolean validateCaptcha(String captchaKey, String captchaValue)
throws AccountCaptchaException {
String text = captchaMap.get(captchaKey);
if (text == null) {
throw new AccountCaptchaException("captaha key"+captchaKey+"not found");
}
if (text.equals(captchaValue)) {
captchaMap.remove(captchaKey);
return true;
}else {
return false;
} } public List<String> getPreDefinedTexts() { return preDefinedTexts;
} public void setPreDefinedTexts(List<String> preDefinedTexts) {
this.preDefinedTexts = preDefinedTexts; } }

 afterPropertiesSet会在framework初始化的时候调用,这个方法初始化验证码生成器,并提供默认配置。

generateCaptchaKey()首先生成一个随机的验证码主键,每个主键将和一个验证码字符串相关联,然后这组关联会被存储到中captchaMap以备将来验证。主键的目的仅仅是标识验证码图片,其本身没有实际的意义。getCaptchaText()用来生成验证码字符串,当preDefinedTexts存在或者为空的时候,就是用验证码图片生成器producer创建一个随机的字符串。当preDefinedTexts,不为空的时候,就顺序地循环该字符串列表读取值。preDefinedTexts有其对应的一组get和stet方法,这样就能让用户预定义验证码字符串的值。generateCaptchaImage方法就能通过producer来生成一个Bufferedlmage ,随后的代码将这个图片对象转换成jpg格式的字节数组并返回。有了该字节数组,用户就能随意地将其保存成文件,或者在网页上显示。

用户得到了验证码图片以及主键后。就会识别图片中所包含的字符串信息,然后将此验证码的值与主键一起反馈给 validateCaptcha方法以进行验证。

测试代码:

public class AccountCaptchaServiceTest {
private AccountCaptchaService service;
@Before
public void prepare() throws Exception{
ApplicationContext ctx = new ClassPathXmlApplicationContext("account_captcha.xml");
service = (AccountCaptchaService) ctx.getBean("accountCaptchaService");
}
@Test
public void testGenerateCaptcha() throws Exception{
String captchaKey = service.generateCaptchaKey();
assertNotNull(captchaKey); byte[] captchaImage = service.generateCaptchaImage(captchaKey);
assertTrue(captchaImage.length>0); File image = new File("target"+captchaKey +".jpg");
OutputStream output = null;
try {
output = new FileOutputStream(image);
output.write(captchaImage);
} finally {
if (output != null) {
output.close();
}
}
assertTrue(image.exists() && image.length()>0);
}
@Test
public void testValidateCaptchaCorrect() throws Exception{
List<String> preDefinedTexts = new ArrayList<String>();
preDefinedTexts.add("12345");
preDefinedTexts.add("abcde");
service.setPreDefinedTexts(preDefinedTexts);
String captchaKey = service.generateCaptchaKey();
service.generateCaptchaImage(captchaKey);
assertTrue(service.validateCaptcha(captchaKey, "12345"));
captchaKey = service.generateCaptchaKey();
service.generateCaptchaImage(captchaKey);
assertTrue(service.validateCaptcha(captchaKey, "abcde"));
}
@Test
public void testValidateCaptchaIncorrect()
throws Exception
{
List<String> preDefinedTexts = new ArrayList<String>();
preDefinedTexts.add("12345");
service.setPreDefinedTexts(preDefinedTexts);
String captchaKey = service.generateCaptchaKey();
service.generateCaptchaImage(captchaKey);
assertFalse(service.validateCaptcha(captchaKey, "67809"));
}
}

  

public class RandomGeneratorTest {
@Test
public void testGetRandomTest() throws Exception{
Set<String> randoms = new HashSet<String>(100);
for (int i = 0; i < 100; i++) {
String random = RandomGenerator.getRandomString();
assertFalse(randoms.contains(random));
randoms.add(random);
} } }

  这个测试代码比较容易看懂。运行测试后可以在项目的target目录下看到生成的验证码图片。

maven小项目注册服务(二)--captcha模块的更多相关文章

  1. maven小项目注册服务(三)--web模块

    java的web应用打包方式一般为war它和jar的区别就是包含了更多的资源和文件,如JSP文件,静态资源文件,servlet等等.war包的核心就WEB-INF文件夹下必须有一个web.xml 的配 ...

  2. maven小项目注册服务(一)--email和persist模块

    跟着书里的讲解,跟着做了一遍该项目: 首先明白注册账户的需求: 账号的lD和Email地址都可以用来唯一地标识某个用户,而显示名称则用来显示在页面下,方便浏览.注册的时候用户还需要输入两次密码,以确保 ...

  3. Android小项目之十二 设置中心的界面

    ------- 源自梦想.永远是你IT事业的好友.只是勇敢地说出我学到! ---------- 按惯例,写在前面的:可能在学习Android的过程中,大家会和我一样,学习过大量的基础知识,很多的知识点 ...

  4. idea结合maven小项目

    整体构造 (修改 POM 文件 )parent <?xml version="1.0" encoding="UTF-8"?> <project ...

  5. spring+springmvc+hibernate架构、maven分模块开发样例小项目案例

    maven分模块开发样例小项目案例 spring+springmvc+hibernate架构 以用户管理做測试,分dao,sevices,web层,分模块开发測试!因时间关系.仅仅測查询成功.其它的准 ...

  6. 基于dubbo构建分布式项目与服务模块

      关于分布式服务架构的背景和需求可查阅http://dubbo.io/.不同于传统的单工程项目,本文主要学习如何通过maven和dubbo将构建分布项目以及服务模块,下面直接开始. 创建项目以及模块 ...

  7. Nacos笔记01——使用Nacos作为SpringCloud项目的服务注册中心

    前言 刚学SpringCloud时使用eureka作为服务注册中心,随着网飞公司eureka2.x不再更新,以及最近在公司实习接触到的SpringCloud项目是使用Nacos来做服务注册中心的,所以 ...

  8. .net core grpc consul 实现服务注册 服务发现 负载均衡(二)

    在上一篇 .net core grpc 实现通信(一) 中,我们实现的grpc通信在.net core中的可行性,但要在微服务中真正使用,还缺少 服务注册,服务发现及负载均衡等,本篇我们将在 .net ...

  9. Dubbo+Zookeeper+SpringMVC+Maven整合实现微服务项目

    互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,Dubbo是一个分布式服务框架,在这种情况下诞生的.现在核心业务抽取出来,作为独立的服务,使 ...

随机推荐

  1. android开发 socket接收图片并保存

    逻辑:接收到socket之后需要将socket发送的图片数据保存下来并通知handler更新界面 关键代码: public void readImage(Socket socket) { try { ...

  2. 在云服务器搭建WordPress博客(三)安装WordPress

    WordPress的安装在Windows环境下非常简单. 安装前的准备 1.下载最新版的 WordPress,解压后,将WordPress文件夹里面的所有文件,上传到你的主机空间域名所绑定的根目录. ...

  3. iOS上用FTGL显示定制Truetype字体碰到的问题

    没想到这个问题搞了快2个月时间:当然跟我只是断断续续地工作有关. FTGL是freetype的opengl实现.我接触FTGL最初只是为了练习OpenGL,写几个简单的游戏app.开始试了试FTGL觉 ...

  4. C++实现简单的内存池

    多进程编程多用在并发服务器的编写上,当收到一个请求时,服务器新建一个进程处理请求,同时继续监听.为了提高响应速度,服务器采用进程池的方法,在初始化阶段创建一个进程池,池中有许多预创建的进程,当请求到达 ...

  5. java和javascript真的有关系=。=

    相同点:1. 内存管理,两者都采用GC来对内存进行回收.因此Java与javascript的内存泄露情况十分相似. 2. 代码编译为机器码后由中间件执行:Java使用前会编译为字节码后由JVM执行,V ...

  6. matlab fscanf用法

    matlab fscanf用法 matlab中的fscanf的用法如下: A=fscanf(fid,format)[A, count]=fscanf(fid,format,size) [A, coun ...

  7. 【WCF--初入江湖】04 WCF通信模式

    04 WCF通信模式 WCF的通信模式有三种 [1]请求响应模式: 只能是客户端调用服务器; 客户端请求并等待服务器的响应后才继续执行后续操作(异步调用除外) [2]单工模式: 只能是客户端调用服务器 ...

  8. 1> Strut2 Mapping to MVC

    CONTROLLER—FILTERDISPATCHER We’ll start with the controller. It seems to make more sense to start th ...

  9. hdu 1879 继续畅通工程(最小生成树,基础)

    题目 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<string.h> #include<algo ...

  10. POJ 1182 食物链(种类并查集)

    记得第一次做这道题的时候,推关系感觉有点复杂,而且写完代码后一直WA,始终找不出错误. 在A了十几道并查集后,再做这道题,发现太小儿科了.发现原来之所以WA,就在于查找根节点时,没有同步更新子节点相对 ...