Ok, so I ended up using Cage Captcha generator. It can be integrated with Maven and it's fairly easy to be implemented with a Spring MVC application with JQuery AJAX.
/**
* Generates captcha as image and returns the image path
* stores the captcha code in the http session
* and deletes older, unused captcha images.
*/
@RequestMapping(value = "/captcha/generate", method = RequestMethod.GET, produces="application/json")
@ResponseBody
public ResponseEntity<CaptchaRequestData> generateCaptcha(HttpSession session) {
String captchaImageUploadDirectory = environment.getProperty("captcha_image_folder");
String captchaWebAlias = environment.getProperty("captcha_web_alias");
//Creating dir or making new one if it doesn't exist
File file = new File(captchaImageUploadDirectory);
if (!file.exists()) {
try {
file.mkdirs();
} catch(Exception e){}
}
String timeSuffix = DBUtils.getDateTimeAsString();
String fileName = CAPTCHA_IMAGE_PREFIX + timeSuffix + "." + CAPTCHA_IMAGE_EXTENSION;
String fullFilename = captchaImageUploadDirectory + fileName;
//Generating the captcha code and setting max length to 4 symbols
Cage currGcage = new YCage();
String captchaToken = currGcage.getTokenGenerator().next();
if (captchaToken.length() > CAPTCHA_CODE_MAX_LENGTH) {
captchaToken = captchaToken.substring(0, CAPTCHA_CODE_MAX_LENGTH).toUpperCase();
}
//Setting the captcha token in http session
session.setAttribute("captchaToken", captchaToken);
try {
OutputStream os = new FileOutputStream(fullFilename, false);
currGcage.draw(captchaToken, os);
os.flush();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
deleteFilesOlderThan(captchaImageUploadDirectory, CAPTCHA_IMAGE_LIFE_MILLISECONDS, CAPTCHA_IMAGE_EXTENSION);
CaptchaRequestData data = new CaptchaRequestData(captchaWebAlias + fileName);
return new ResponseEntity<>(data, HttpStatus.OK);
}
Then when I'm creating the object I check if the given code equals the one, stored in the session:
if (!httpSession.getAttribute("captchaToken").equals(bindingData.getCaptchaCode())) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
Finally if the provided captcha is incorrect I generate a new one.