[시큐어코딩실습] 로그인 시도 횟수 제한
로그인 시도 횟수 제한시 고려해야 할 항목들을 정리해 보면,
- 로그인 시도 횟수 : 로그인 시도시 마다 1씩 증가 시켜, 시도 횟수가 5보다 큰 경우 일정 시간 동안
로그인 기능이 잠금 상태가 되도록 한다.
- 마지막으로 실패한 로그인 시간 정보 : 이 시간 정보를 이용하여 로그인 잠금 상태가 유지 되어야 하는
시간을 설정 할 수 있다.
- 마지막으로 성공한 로그인 시간 정보 : 이 시간 정보를 이용하여 마지막으로 로그인한 시간 정보를
사용자에게 알려줌으로써 자기 계정이 혹시 다른 사람에 의해 사용되었는지 여부를
사용자가 점검 할 수 있도록 한다.
- 로그인한 사용자의 IP 정보 : 이 정보를 관리하므로 해서 다른 IP에서 중복 로그인을 하지 못하도록 설정
할 수 있다. 일반적으로 뒤에 로그인되는 사용자에게 중복 로그인에 대한 정보를 제공
하고, 이전 로그인 세션의 강제 종료를 설정할 수 있도록 한다.
package kr.co.openeg.sec.lab.login.controller;
import java.util.Date;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import kr.co.openeg.sec.lab.login.model.LoginSessionModel;
import kr.co.openeg.sec.lab.login.service.LoginService;
import kr.co.openeg.sec.lab.login.service.LoginValidator;
import kr.co.openeg.sec.lab.member.model.SecurityModel;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class LoginController {
@Resource(name="loginService")
private LoginService service;
@RequestMapping("/login.do")
public String login() {
return "/board/login";
}
@RequestMapping(value="/login.do", method = RequestMethod.POST)
public ModelAndView loginProc(@ModelAttribute("LoginModel")
LoginSessionModel loginModel,
BindingResult result,
HttpServletRequest request,
HttpSession session) {
ModelAndView mav = new ModelAndView();
// form validation
new LoginValidator().validate(loginModel, result);
if(result.hasErrors()){
mav.setViewName("/board/login");
return mav;
}
String userId = loginModel.getUserId();
SecurityModel sec = service.checkSecurity(userId);
System.out.println("retry: "+sec.getRetry());
// 로그인 시도횟수가 5회가 넘으면 30초간 로그인 잠금
if ( sec.getRetry() >= 5 ) {
if (new Date().getTime() - sec.getLastFailedLogin() < 30000) {
mav.addObject("userId", userId);
mav.addObject("errCode", 6); // 30초동안 로그인잠금 알림
mav.setViewName("/board/login");
return mav;
} else {
sec.setRetry(0);
sec.setLastFailedLogin(0);
service.updateSecurity(sec);
}
}
String userPw = loginModel.getUserPw();
LoginSessionModel loginCheckResult =
service.checkUserId(userId,userPw);
// 로그인이 틀리면, 로그인 시도횟수를 1증가 시키고,
// 로그인 실패 시간을 DB에 업데이트 한다.
if(loginCheckResult == null){
sec.setRetry(sec.getRetry()+1);
sec.setLastFailedLogin(new Date().getTime());
service.updateSecurity(sec);
mav.addObject("retry",sec.getRetry());
mav.addObject("userId", userId);
mav.addObject("errCode", 1); // not exist userId in database
mav.setViewName("/board/login");
return mav;
}else {
// 로그인이 성공하면, 로그인 시도횟수를 0로 reset,
// 마지막으로 로그인 실패 시간 0으로 reset
// 성공한 클라이언트 IP를 DB에 업데이트,
// 로그인 성공시간 DB에 업데이트
sec.setRetry(0);
sec.setLastFailedLogin(0);
sec.setLastSuccessedLogin(new Date().getTime());
sec.setclientIp(request.getRemoteAddr());
service.updateSecurity(sec);
session.setAttribute("userId", userId);
session.setAttribute("userName", loginCheckResult.getUserName());
mav.setViewName("redirect:/board/list.do");
return mav;
}
}
@RequestMapping("/logout.do")
public String logout(HttpSession session){
session.invalidate();
return "redirect:login.do";
}
}
소스파일: LoginController.java
로그인 세션 관리
http://blog.naver.com/kyong94s/119737344