로그인 횟수 제한 설정
(1) 로그인을 처리에 사용되는 클래스들
kr.co.openeg.lab.login.controller.LoginController.java
: login.do 요청을 처리하는 컨트롤러 컴포넌트
kr.co.openeg.lab.login.service.LoginService.java
: 로그인 요청을 처리하는 서비스 컴포넌트
kr.co.openeg.lab.login.dao.LoginDao.java
: 로그인 요청에 대한 DB CRUD 처리 규격을 정의하고 있는 인터페이스
kr.co.openeg.lab.login.dao.LoginDaoImpl.java
: 로그인 요청에 대한 DB 처리 구현 클래스
kr.co.openeg.lab.common.interceptor.SessionInterceptor.java
: 시스템 연결 요청에 대한 세션 관리를 위한 Interceptor 클래스
kr.co.openeg.lab.member.model.SecVO.java
: 오픈이지 시스템 사용자에 대한 보안 정책을 저장하는 모델 컴포넌트
login.jsp
: 로그인 요청 처리 UI 뷰 컴포넌트
(2) 사용자 보안 정보를 담는 그릇 SecVO
package kr.co.openeg.lab.member.model;
// board_security 테이블 정보를 사용하기 위한 모델 컴포넌트
public class SecVO {
private String userId;
private String username;
private String salt;
private String secKey;
private int loginStatus;
private int loginFailedCount;
private long lastFailedLogin;
private long lastSuccessedLogin;
public SecVO() {}
public SecVO(String userId, String username, String salt, String secKey,
int loginStatus, int loginFailedCount,
long lastFailedLogin, long lastSuccessedLogin) {
super();
this.userId = userId;
this.username = username;
this.salt = salt;
this.secKey = secKey;
this.loginStatus = loginStatus;
this.loginFailedCount = loginFailedCount;
this.lastFailedLogin = lastFailedLogin;
this.lastSuccessedLogin = lastSuccessedLogin;
}
/* private 멤버 변수에 대한 getter/setter 메서드 선언 */
(3) SessionInterceptor을 이용한 요청 수락/차단 정책 설정
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)
throws Exception {
// 세션에 저장된 보안 정책을 읽어온다.
SecVO secVO= (SecVO)request.getSession().getAttribute("secVO");
// 최초 접속인 경우 보안정책 모델을 생성하여 세션에 저장한다.
if(secVO == null) {
secVO=new SecVO();
request.getSession().setAttribute("secVO", secVO);
}
// login.do나 join.do 페이지 요청인 경우
if(request.getRequestURI().equals("/openeg/login.do") ||
request.getRequestURI().equals("/openeg/member/join.do")){
// 로그인 상태를 확인하고 이미 로그인 한 사용자 이면 리스트 페이지로 이동
if(secVO.getLoginStatus() ==1 ){
response.sendRedirect(request.getContextPath() + "/board/list.do");
return true;
} else {
// 로그인을 요청하거나 회원가입을 요청하면 해당 페이지로 이동
return true;
}
}
// 로그인 하지 않은 사용자가 로그인이나 회원가입 이외의 페이지를
// 요청한 경우 로그인 페이지로 이동
if(secVO.getLoginStatus() == 0){
response.sendRedirect(request.getContextPath() + "/login.do");
HttpSession session=request.getSession();
session.setAttribute("errCode", "4");
return false;
} else {
// 이미 로그인한 사용자는 해당 페이지로 이동
return true;
}
}
(4) LoginController에서 로그인 시도횟수 제한
@RequestMapping(value="/login.do", method = RequestMethod.POST)
public ModelAndView loginProc(@ModelAttribute("LoginModel")
LoginSessionModel loginModel, BindingResult result,
HttpSession session) {
ModelAndView mav = new ModelAndView();
SecVO secVO=(SecVO)session.getAttribute("secVO");
String userId = loginModel.getUserId();
String userPw = loginModel.getUserPw();
// 처음 로그인 시도이면 secVO에 보안정책 설정
if ( secVO.getUserId() == null ) {
secVO=service.getSecurityInfo(userId);
}
int tryCount=secVO.getLoginFailedCount();
// 로그인 시도 횟수가 5회 이상이면 10초 간격을 두고 다시
// 로그인을 수행하도록 설정
if ( tryCount >= 5 ) {
if ( getCurrentTime() - secVO.getLastFailedLogin() < 10000 ) {
mav.addObject("errCode", 5);
mav.setViewName("/board/login");
return mav;
}
}
// 입력값 검증이 실패하면 tryCount를 1증가 시키고 로그인 화면으로 이동
new LoginValidator().validate(loginModel, result);
if(result.hasErrors()){
secVO.setLoginFailedCount(++tryCount);
session.setAttribute("secVO", secVO);
mav.setViewName("/board/login");
return mav;
}
// 로그인 처리(DB 쿼리 수행)
LoginSessionModel loginCheckResult =
service.checkUserId(userId,userPw);
//로그인이 실패했으면
if(loginCheckResult == null){
secVO.setLoginFailedCount(++tryCount);
secVO.setLastFailedLogin(new Date().getTime());
session.setAttribute("secVO", secVO);
mav.addObject("errCode", 1); // not exist userId in database
mav.setViewName("/board/login");
return mav;
}else {
// 로그인이 성공했으면
secVO.setLoginFailedCount(0);
secVO.setLastSuccessedLogin(new Date().getTime());
secVO.setLoginStatus(1);
session.setAttribute("secVO", secVO);
mav.setViewName("redirect:/board/list.do");
return mav;
}
}
(5) 로그인 화면(login.jsp)
<div id="aside">
<spring:hasBindErrors name="LoginModel" />
<form:errors path="LoginModel" />
<form action="login.do" method="post">
<fieldset>
<center>
<label for="userId">사용자명 : </label>
<input type="text" id="userId" name="userId" class="loginInput"
value="${secVO.userId}" />
<span class="error">
<form:errors path="LoginModel.userId" />
</span><br />
<label for="userPw">비밀번호 : </label>
<input type="password" id="userPw" name="userPw"
class="loginInput" />
<span class="error"><form:errors path="LoginModel.userPw" />
</span><br />
<br />
<input type="submit" value="로그인" class="submitBt" />
<input ype="button" value="회원가입" class="submitBt"
onClick='window.open("member/join.do","_blank",
"width=400,height=400, toolbar=no, menubar=no,
scrollbars=no, resizable=no, copyhistory=no");' />
</center>
</fieldset>
</form>
</div>
(6) 로그인 성공후 보여지는 화면(list.jsp)
<div id="aside">
<fieldset>
<center>
<label>[ ${secVO.userName} ]님 환영합니다.</label><br/>
<a href="../logout.do">로그아웃</a>
<a href="../member/modify.do">정보수정</a>
</center>
</fieldset>
</div>
플로우차트 그리기:
'보안 > 시큐어코딩' 카테고리의 다른 글
시큐어코딩세미나 (0) | 2014.11.04 |
---|---|
[시큐어코딩실습] 세션관리 취약점 제거 (0) | 2014.10.22 |
자바에서 파일 타입 확인하기 (0) | 2014.10.08 |
JAVA 시큐어코딩 정오표 (0) | 2014.10.05 |
spring 설정파일의 설정값 암호화 하기 (0) | 2014.07.29 |