Develop

[JAVA]구글 OTP 구현 + QR코드 본문

웹 개발/Java

[JAVA]구글 OTP 구현 + QR코드

개발 기록 2024. 9. 4. 11:16

 

serviceImpl 

	@Override
	public boolean otpLogin(SOMap rmap) throws Exception {
		
		SOOMap var = Util.getSOOVmap(rmap);
		SOOMap dbparams = new SOOMap();
		String userNo = p.getStr("userNo");
		
		dbparams.put("userNo", userNo);
		String otpKey = userDao.selectOtpKey(dbparams);
		
		if (Util.isNotEmpty(userNo) && Util.isEmpty(otpKey)) {
			var.put("otpKeyYn", "N");
			
			// 개인키 생성
			String privatekey = GoogleOTPUtil.getSecretKey();
			
			// 주소
			String URL = GoogleOTPUtil.getGoogleOTPAuthURL(privatekey, "유저아이디", "사이트이름");
			// QRCode 생성
			String img =  GoogleOTPUtil.getQRImage(URL, 200, 200);
			
			// 개인키 db저장
			dbparams.put("otpKey", privatekey);
			userDao.updateOtpKey(dbparams);
			
			var.put("img", img);
			var.put("URL", URL);
			var.put("privatekey", privatekey);
		}else{
			var.put("otpKeyYn", "Y");
		}
		
		return true;
	}
	
	@Override
	public boolean otpLoginAct(SOMap p ,SOMap rmap) throws Exception {
		
		SOMap var = Util.getSOVmap(rmap);
		SOMap dbparams = new SOMap();
		
		String userNo = p.getStr("userNo"); // 유저번호
		String otpNum = p.getStr("otpNum"); // 유저가 입력한 값
		
		if(Util.isEmpty(userNo)){
			rmap.put(Const.D_SCRIPT, Util.jsmsgLink("잘못된 접근입니다.", "/manager/main/index.do", "T"));
			return false;
		}
		
		if(Util.isEmpty(otpNum)){
			rmap.put(Const.D_SCRIPT, Util.jsmsgLink("인증번호를 입력해주세요.", "/manager/main/otp_login.do", "T"));
			return false;
		}
		
		// 개인키 값 가져오기
		dbparams.put("userNo", userNo);
		String otpKey = userDao.selectOtpKey(dbparams); // 개인키
		
		String code = GoogleOTPUtil.getTOTPCode(otpKey);
	
		if(!code.equals(otpNum)){
			return false;
		}
		return true;
	
	}

 

 


구글 검색시 나오는 코드를 참조하였고

QR코드만 생성이 안되길래 코드를 조금 바꿨다
* import 확인필수(Base64 import를 잘못해서 한참 해맸음 ㅠ)

package apps.utils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.codec.binary.Base32;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;

import de.taimos.totp.TOTP;

/**
 * 구글 OTP 관련 클래스
 */
public class GoogleOTPUtil {
	
	// 최초 개인키 생성
	public static String getSecretKey(){
		SecureRandom random = new SecureRandom();
		byte[] bytes = new byte[20];
		random.nextBytes(bytes);
		Base32 base32 = new Base32();
		return base32.encodeToString(bytes);
	}
	
	// OTP검증 요청 시 확인
	public static String getTOTPCode(String secretKey){
		Base32 base32 = new Base32();
		byte[] bytes =  base32.decode(secretKey);
		String hexKey = String.valueOf(Hex.encodeHex(bytes));
		return TOTP.getOTP(hexKey);
	}
	
	// 개인키, 계정명(유저 아이디), 발급자(회사명or소속)을 받아서 구글OTP 인증용 링크를 생성하는 메소드
	public static String getGoogleOTPAuthURL(String secretKey, String account, String issuer){
		try{
			return "otpauth://totp/"
					+ URLEncoder.encode(issuer + ":" + account, "UTF-8").replace("+", "%20")
					+ "?secret=" + URLEncoder.encode(secretKey, "UTF-8").replace("+", "%20")
					+ "&issuer=" + URLEncoder.encode(issuer, "UTF-8").replace("+", "%20");
		}catch(UnsupportedEncodingException e){
			throw new IllegalStateException(e);
		}
	}
	
	// url, 파일 생성할 경로를 받아서 QR코드 이미지 생성
	public static String getQRImage(String otpUrl, int height, int width) throws WriterException, IOException {
		
	    QRCodeWriter qrCodeWriter = new QRCodeWriter();
        Map<EncodeHintType, Object> hints = new HashMap<>();
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        
	    BitMatrix bitMatrix = qrCodeWriter.encode(otpUrl, BarcodeFormat.QR_CODE, width, height, hints);
        ByteArrayOutputStream pngOutputStream = new ByteArrayOutputStream();
        MatrixToImageWriter.writeToStream(bitMatrix, "PNG", pngOutputStream);
        byte[] qrCodeImage = pngOutputStream.toByteArray();
		qrCodeImage = Base64.encodeBase64(qrCodeImage);
		String img = new String(qrCodeImage, "UTF-8");
        
        return img;
    
    }

}

 

 

 

뷰페이지에서 출력이 고민된다면

 

https://sseb32310.tistory.com/133


 

참고한 글

 

https://medium.com/@ihorsokolyk/two-factor-authentication-with-java-and-google-authenticator-9d7ea15ffee6

 

Two-Factor Authentication with Java and Google Authenticator

I am more than sure that each of you have at least one account with enabled Two-Factor Authentication (2FA). But if you are still…

medium.com

 

https://creampuffy.tistory.com/89

 

Google OTP 인증 Java로 구현하기

OTP는 One Time Password의 약자이다. 일회용이라는 측면에서 보안에 더욱 강력하다고 알려져 있다. 자바에서 구현하기 위해 여러 링크를 둘러봤는데 아래 링크가 가장 크게 도움이 됐다. medium.com/@ihor

creampuffy.tistory.com