최근 맡고 있는 프로젝트에서 로그인 보안을 강화해 달라는 요청이 들어왔다.
한 개의 아이디로 여러 사람이 로그인하는 경우가 많은데, 현재 업무를 하지 않는 사람들도
해당 아이디로 로그인 할 수 있어 보안이 취약하다는 점이었다.
(비밀번호를 자주 바꾸지 않는 기관인듯싶다.)
그래서 어떤 방법으로 로그인 보안을 강화시킬 수 있을까 고민했고
Google aouth2.0 방법의 2차 인증을 도입하기로 했다.
Google 인증 방식도 여러 방법이 있었으나 Google Authenticator앱을 사용한
OTP인증 방식을 채택했다.
1. 의존성 주입
구글에서 제공해 주는 라이브러리가 있었고
pom.xml에 위와 같이 의존성을 주입했다.
2. OTP 인증 화면(JSP)
$("#LoginSubmit").on("click", function() {
var otpCode = $("#otpCode").val();
if($.trim($("#otpCode").val()) == "")
{
alert("인증코드 6자리를 입력해 주십시오.");
return false;
}
// 1. 6자리 이상 입력 금지
if (otpCode.length > 6) {
alert("OTP 코드는 6자리 이하여야 합니다.");
$("#otpCode").focus();
return;
}
// 2. ' 입력 금지
if (otpCode.includes("'")) {
alert("OTP 코드에는 ' 문자를 포함할 수 없습니다.");
$("#otpCode").focus();
return;
}
// 3. <,> 스크립트 태그 입력 금지
if (otpCode.includes('<') || otpCode.includes('>')) {
alert("OTP 코드에는 <,> 문자를 포함할 수 없습니다.");
$("#otpCode").focus();
return;
}
// 4. 숫자만가능
if (!/^[0-9]+$/.test(otpCode)) {
alert("OTP 코드는 숫자로만 입력해주세요.");
$("#otpCode").focus();
return;
}
$.ajax({
async : false,
type: "post",
url: "${contextPath}/portal/checkCode.text",
//data: "encodedKey=" + $("#encodedKey").val() + "&otpCode=" + $("#otpCode").val(),
data: {
encodedKey : $("#encodedKey").val(),
otpCode : $("#otpCode").val()
},
dataType: "text",
success: function (dataObj) {
if(dataObj == "1"){
alert("2차인증 성공했습니다.");
}else {
alert("2차인증 실패했습니다..");
}
},
error: function(xhr, textStatus, error) {
//생략
}
//html구조
<div class="loginbox">
<h3 class="mb_10 fw-normal" style="font-weight: bold; color: black; font-size: 24px;">OTP 인증</h3>
<div class="line" style="margin-top: 20px;">
<ul>
<li style="margin-bottom: 20px;">
<span style="font-weight: bold;">키 인증 번호</span>
<input type="text" id="encodedKey" name="encodedKey" value="${otp.otpKey}" readonly="readonly" style="margin-left: 10px;"/>
</li>
<li style="margin-bottom: 20px;">
<span style="font-weight: bold;" >QR코드</span>
<img src="${otp.qrUrl}" alt="OTP QR Code" style="width: 100px; height: 100px; margin-left: 13px;">
</li>
<li style="margin-bottom: 20px;">
<span style="font-weight: bold;">코드 입력 창</span>
<input type="text" id="otpCode" name="otpCode" value="" style=" width: 90px; margin-left: 10px;"/>
</li>
</ul>
<button type="button" id="LoginSubmit2">코드제출</button>
</div>
#LoginSubmit버튼 클릭 시 유효성 검사를 하는
로직과 api를 호출하는 로직을 작성했다.
input 화면에서 보안을 강화하기 위해 '(작은따옴표), <>(스크립트태그)
입력이 안되도록 했다.
3. Controller
//로그인 화면에서 id,pw 입력 후 로그인 성공하면 otp화면으로 이동하는 api
@RequestMapping(value = "/otpLogin")
public String otp(ModelMap model) {
String viewName = "/login/otpLogin";
//otp등록 키 생성
Map<String, Object> otpMap = loginService.createOtpKey();
model.addAttribute("otp", otpMap);
return viewName;
}
//otp인증 화면에서 앱에서 받은 6자리 인증코드가 유효한지 체크하는 api
@RequestMapping(value = "/checkCode", method = RequestMethod.POST)
public void checkCode( String encodedKey, String otpCode, ModelMap model) {
int result = 0;
//화면에서 입력 받은 6자리코드가 발급받은 key와 유효한지 체크
boolean isValid = loginService.checkOtpCode(encodedKey,otpCode);
if(isValid == true){
result = 1;
}else {
result = 2;
}
model.addAttribute("attribute", result);
}
4. Service
//otp키 생성
public Map<String, Object> createOtpKey(){
Map<String, Object> otpMap = new HashMap<String, Object>();
boolean hasSession = UserSession.hasSession();
if (hasSession) {
String userId = UserSession.getUserId();
GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator();
GoogleAuthenticatorKey googleAuthenticatorKey = googleAuthenticator.createCredentials();
String key = googleAuthenticatorKey.getKey();
String QRUrl = GoogleAuthenticatorQRGenerator.getOtpAuthURL("my firstWeb", userId, googleAuthenticatorKey);
otpMap.put("otpKey", key);
otpMap.put("qrUrl", QRUrl);
}
return otpMap;
}
//인증코드 유효체크
public boolean checkOtpCode(String key, String otpCode){
GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator();
//화면에서 받은 otp코드가 발급받은 key와 대조하여 유효한지 체크
boolean isValid = googleAuthenticator.authorize(key, Integer.parseInt(otpCode));
return isValid;
}
서비스에 구현된 메서드다.
첫 번째 메서드에서는 key를 생성하고 앱에서 key등록 시 필요한 qr코드를 생성하여 화면단으로 return
두 번째 메서드에서는 화면단에서 받은 6자리의 인증코드를 key와 대조하여 유효한지 체크
5. 구현화면
1. 화면
2.google 인증 앱 화면
6. 구글 otp로그인 전체 설명
위 구현 코드 설명
1. 로그인 성공하면 otp인증화면으로 이동하고 키, qr코드를 생성하며 화면에 뿌린다.
2. App에서 QR코드를 인식하면 자동으로 key값을 인식하고 key에 해당하는 6자리 코드를 생성한다.
3. otp인증하면에 6자리 코드를 입력 후 코드제출 버튼을 누르면 서버에서 key값과
대조하여 6자리 코드가 유효한지 검사한다.
4. 6자리 코드가 유효하면 main화면이 보인다.
여기까지가 내가 구현한 코드의 설명이고, 실제 서비스에서 구현할 시
몇 가지 더 로직을 구현해야 한다.
1. DB에서 로그인 아이디에 대한 key값을 저장하는 로직,
2. 두 번째 로그인부터는 로그인 아이디에 대해 key가 발급되어 있는지 체크하는 로직.
3. key가 있으면 화면에 키인증 번호, QR코드는 보이지 않게 하는 로직.
4. 6자리 인증코드 유효한지 체크할 시, DB에서 해당 로그인 ID로 발급된 key를 가져오는 로직.
7. 구현 소감
구글에서 제공해주는 라이브러리로 위와 같이 OTP를 활용한 로그인 보안을 강화 할 수 있었다.
구현 하기 전에는 서버와 앱이 계속 통신을 해야 하는건가? 싶었는데
알고 보니 서버와 앱은 계속 통신하는것이 아닌
서버에 등록된 Key, 앱에 등록된 Key가 동일한 Key라서
같은 알고리즘으로 인증코드 생성, 체크 한다는 것을 알게 되었다.
위와 같은 사실을 알고나서 코드를 작성하니 어렵지 않게 OTP인증 로직을 구현 할 수 있었다.
'전자정부프레임워크&Spring Project' 카테고리의 다른 글
톰캣서버에 다수의 프로젝트 배포하기(War파일) (0) | 2024.08.04 |
---|---|
Interceptor로 외부IP 접근 차단 (0) | 2024.07.20 |
다량의 데이터 batch처리로 한번에 처리하기 (0) | 2024.05.27 |
전자정부프레임워크 - 수료증 (1) | 2024.03.19 |
전자정부프레임워크 - JWT(Json Web Token)인증방식 구현, 세션아웃 처리 (2) | 2024.03.17 |