본문 바로가기

Java

simplecaptcha 한국어 음성 적용하기

simplecaptcha를 사용하여 보안 문자를 만들다가 문득 생각해보니 음성이 영어다...


웹접근성을 생각하면 한국어 음성이 필요하다. 인터넷을 찾아보니 2가지 소스가 나왔는데,


스프링 부트를 이용했다는 건 뭘한건지 모르겠는데 안된다.


그리고 직접 음성 소스를 넣은게 있어서 참고했다. 직접하려면 삽질좀 했을거 같은데 다른 사람이 만들어 둔 내용을 참고하니 역시 편하다. 나는 숫자 5개를 이용하여 보안 문자를 만들 것이다.


먼저 이미지를 만들어보자.


public static void makeTextImage(HttpServletRequest request, 

HttpServletResponse response, String textType) 

throws ServletException, IOException {

List<java.awt.Color> textColors = Arrays.asList(Color.DARK_GRAY);

List<java.awt.Font> textFonts = Arrays.asList(

new Font("Arial", Font.ITALIC, 40), 

new Font("Courier", Font.PLAIN, 40));

Captcha captcha = new Captcha.Builder(140, 60)

.addText(new NumbersAnswerProducer(5), new DefaultWordRenderer(textColors,textFonts))

.gimp()

.addBackground()

.build();

request.getSession().setAttribute("CAP_ANSWER", captcha.getAnswer());

CaptchaServletUtil.writeImage(response, captcha.getImage());

}


이미지를 생성하는 메서드이다. simplecaptcha 사이트의 예제로도 있으니 넘어가겠다.


public static void makeTextAudio(HttpServletRequest request, 

HttpServletResponse response, String textType) 

throws ServletException, IOException {


String capAnswer = (String)request.getSession().getAttribute("CAP_ANSWER");

if(capAnswer != null && !"".trim().equals(capAnswer)){

SetKorVoiceProducervProd = new SetKorVoiceProducer(); 

AudioCaptcha ac = new AudioCaptcha.Builder()

.addAnswer(new SetTextProducer(capAnswer))

.addVoice(vProd)

//.addNoise()

.build();

CaptchaServletUtil.writeAudio(response, ac.getChallenge());

}else{

// 생성된 이미지가 없음

}

}


마찬가지로 예제의 내용이 대부분인데, 없는게 2가지 있다. SetKorVoiceProducer클래스와 SetTextProducer 클래스이다. 


SetTextProducer 클래스는 AudioCaptcha에 값을 셋팅해주려면 반드시 만들어야한다. 클래스로만 셋팅할 수 있게 되어있어서 어쩔 수 없이 생성해야하는 클래스이다. 인터페이스를 상속받아서 사용하면 된다.


SetKorVoiceProducer 클래스는 음성파일을 별도로 지정할 수 있다. 이 부분은 아래 사이트를 참고했다.


참고: http://blog.naver.com/javaking75/220276964082


음성파일을 따로 구하기가 뭐해서 사이트에서 음성 파일만 추출해서 동일하게 만들어 사용했다. 덕분에 쉽게 만들어서 편했다. 참고 사이트에서 정의된 클래스 내용이다.


public class SetKorVoiceProducer  implements VoiceProducer  {

    

    private static final Map<Integer, String> DEFAULT_VOICES_MAP;

 

    static {

        DEFAULT_VOICES_MAP = new HashMap<Integer, String>();

        StringBuilder sb;

        

        for (int i = 0; i < 10; i++) {            

            sb = new StringBuilder("/sounds/ko/numbers/");

            sb.append(i);            

            sb.append(".wav");            

            DEFAULT_VOICES_MAP.put(i, sb.toString());

        }

    }

    

    private final Map<Integer, String> _voices;

 

    public SetKorVoiceProducer() {

        this(DEFAULT_VOICES_MAP);

    }

 

    public SetKorVoiceProducer(Map<Integer, String> voices) {

        _voices = voices;

    }

 

    @Override

    public Sample getVocalization(char num) {

       try {

            Integer.parseInt(num + "");

        } catch (NumberFormatException e) {

            throw new IllegalArgumentException("Expected <num> to be a number, got '" + num + "' instead.",e);

        }

 

        int idx = Integer.parseInt(num + "");

        String filename = _voices.get(idx); 

        return FileUtil.readSample(filename);

    }

}


심플하게 잘 구현한 것 같다. FileUtil 클래스는 원래 simplecaptcha 패키지 안에 있는 클래스를 이용한 걸로 봐서 기존 소스를 보고 깔끔하게 작업하신 듯하다. 내 시간을 아껴주셔서 감사할 따름이다. 


단지 음성파일 소스가 어떻게 되는지 몰라서 무작정 가져다 쓰기가 곤란하다... 문제가 되면 삭제한다고 했으니 문제는 없어보인다. 음성 소스 출처를 분명이 해줬면 가장 좋을듯한데, 약간 아쉽다.


이제 컨트롤러는 다음과 같이 만들었다.

/*캡챠 이미지*/

@ResponseBody

@RequestMapping("/common/captcha.do")

public void captcha(HttpServletRequest request, HttpServletResponse response) {

try{

CaptchaUtil.makeTextImage(request, response, "numbers");

}catch(Exception ex){

logger.error("/common/captcha.do >>> "+ex.getMessage());

}

}

/*캡챠 오디오*/

@ResponseBody

@RequestMapping("/common/captchaAudio.do")

public void captchaAudio(HttpServletRequest request, HttpServletResponse response) {

try{

CaptchaUtil.makeTextAudio(request, response, "numbers");

}catch(Exception ex){

logger.error("/common/captchaAudio.do >>> "+ex.getMessage());

}

}


메서드는 CaptchaUtil 클래스로 묶고 생성한 URL을 이용하여 화면에 적용한 javascript 소스는 다음과 같다.

function reloadCaptcha(){

var timeData = new Date();

var reloadNum = timeData.getTime();

var captchaUrl = '/common/captcha.do';

$('#captchaImg').attr('src',captchaUrl+'?'+reloadNum);

}


function playAudio(){

var timeData = new Date();

var reloadNum = timeData.getTime();

var browserInfo = navigator.userAgent.toLowerCase();

if((browserInfo.indexOf("msie") >= 0 || browserInfo.indexOf("trident") >= 0)){

var htmlString = '<object type="audio/x-wav" data="/common/captchaAudio.do?' + reloadNum + '" width="0" height="0"><param name="src" value="/common/captchaAudio.do?' + reloadNum +'"/><param name="autostart" value="true" /><param name="controller" value="false" /></object>';

document.getElementById('audioPlayer').innerHTML = htmlString;

document.getElementById('captcha_input').focus();

}else if(browserInfo.indexOf("chrome") >= 0 ||

browserInfo.indexOf("firefox") >= 0 ||

browserInfo.indexOf("safari") >= 0){

var htmlString = '<audio controls autoplay style="height:0px;width:0px;"><source src="/common/captchaAudio.do?' + reloadNum + '" type="audio/wav"></audio>';

document.getElementById('audioPlayer').innerHTML = htmlString;

document.getElementById('captcha_input').focus();

}else{

// 예외처리 필요

}

}


이미지 Tag에 캡챠 이미지를 적용하고 새로고침이 필요하면 reloadCaptcha을 호출한다. 혹시 브라우저 캐시가 방해할 수 있어 임의의 쿼리스트링을 추가 했다. 이건 오디오도 동일하다.


오디오는 브라우저 마다 다른 처리를 해야하는데, 아직 미완성이다. 좀 더 테스트가 필요해보인다.


확인은 세션의 속성을 참고하면 되니 간단하다. 어렵고도 간단하다. 역시 셈플이 최고다.