CaptchaService.java
package com.nonononoki.alovoa.service;
import java.awt.Color;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Date;
import javax.imageio.ImageIO;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.nonononoki.alovoa.entity.Captcha;
import com.nonononoki.alovoa.lib.OxCaptcha;
import com.nonononoki.alovoa.repo.CaptchaRepository;
@Service
public class CaptchaService {
@Autowired
private CaptchaRepository captchaRepo;
@Autowired
private HttpServletRequest request;
@Value("${app.captcha.length}")
private int captchaLength;
@Value("${app.text.salt}")
private String salt;
private static final int WIDTH = 120;
private static final int HEIGHT = 70;
private static final Color BG_COLOR = new Color(0, 0, 0, 0);
private static final Color FG_COLOR = new Color(118, 118, 118);
public Captcha generate() throws NoSuchAlgorithmException, IOException {
String ipHash = getIpHash(request.getRemoteAddr());
Captcha oldCaptcha = captchaRepo.findByHashCode(ipHash);
if (oldCaptcha != null) {
captchaRepo.delete(oldCaptcha);
captchaRepo.flush();
}
OxCaptcha ox = generateCaptchaImage();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(ox.getImage(), "webp", baos);
byte[] ba = baos.toByteArray();
String encoded = Base64.getEncoder().encodeToString(ba);
Captcha captcha = new Captcha();
captcha.setDate(new Date());
captcha.setImage(encoded);
captcha.setText(ox.getText());
captcha.setHashCode(ipHash);
captcha = captchaRepo.saveAndFlush(captcha);
return captcha;
}
private OxCaptcha generateCaptchaImage() {
OxCaptcha c = new OxCaptcha(WIDTH, HEIGHT);
c.foreground(FG_COLOR);
c.background(BG_COLOR);
c.text(captchaLength);
c.distortion();
c.noiseStraightLine();
c.noiseStraightLine();
c.noiseStraightLine();
return c;
}
public boolean isValid(long id, String text) throws UnsupportedEncodingException, NoSuchAlgorithmException {
Captcha captcha = captchaRepo.findById(id).orElse(null);
if (captcha == null) {
return false;
}
captchaRepo.delete(captcha);
captchaRepo.flush();
return captcha.getHashCode().equals(getIpHash(request.getRemoteAddr()))
&& captcha.getText().equalsIgnoreCase(text);
}
private String getIpHash(String ip) throws UnsupportedEncodingException, NoSuchAlgorithmException {
// don't need slow hashing algorithm because
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(salt.getBytes()); // salting to prevent rainbow tables
md.update(ip.getBytes(StandardCharsets.UTF_8));
byte[] bytes = md.digest();
return Base64.getEncoder().encodeToString(bytes);
}
}