maven小项目注册服务(二)--captcha模块

时间:2022-11-26 03:20:42

验证码生成模块,配置信息基本和前面的模块一样。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目录下看到生成的验证码图片。