P662
4.1.3 자동 로그인 구상하기
자동 로그인 처리의 기본 아이디어는 '세션 + 쿠키'를 이용하려 합니다. 보통은 HttpSession만을 이용하거나 쿠키만을 이용하지만, 이 두 가지를 섞어서 사용하는 예제입니다. 예제의 작성 이전에 코드로 작성해야 하는 상황에 대한 이해가 필요합니다.
쿠키와 세션으로 발생하는 상황은 아래의 네 가지 경우가 있을 수 있습니다.
- HttpSession에 login 이름으로 보관된 객체가 없고, loginCookie 가 없는 경우
- 로그인과 관련된 아무런 정보가 없으므로, 사용자는 로그인이 필요한 상황입니다.
- HttpSession에 login 이름으로 보관된 객체가 있고, loginCookie가 없는 경우
- 세션에 login 이라는 이름으로 보관된 경우는 현재 사용자가 로그인한 상황이 확실합니다.
- HttpSession 에 login 이름으로 보관된 객체가 없고, loginCookie가 존재하는 경우
- 사용자는 이전에 로그인을 한 적이 있을 수 있습니다. 사용자가 이전에 로그인을 할 때 loginCookie가 생성되었을 것이고,
어떤 이유로 인해 사용자는 브라우저를 종료했을 것입니다.
- 브라우저가 종료되면서, 세션 쿠키는 사라졌지만, loginCookie 는 5분 동안 보관되므로, loginCookie 가 있다는 것은 5분 사이에
접속한 적이 있다는 것을 의미합니다.
- HttpSession 에 login 이름으로 보관된 객체가 있고, loginCookie가 있는 경우
- 사용자는 현재 접속 중인 사용자입니다.
의 경우 수 중에서 자동 로그인은 'HttpSession 에는 login 이름으로 보관된 객체가 없지만, loginCookie는 존재하는 상황입니다.
이 경우 사용자는 이전에 로그인을 한 적이 있다는 것을 의미하므로, 과거 로그인 시점에 기록된 정보를 이용해서 다시 HttpSession에 login 이름
으로 UserVO 객체를 보관해 줘야 합니다.
이후의 모든 작업은 HttpSession에 login 이름으로 저장된 객체가 있으므로 , 모두 해됩니다.
4.2 자동 로그인의 구현
위의 설명을 요약하자면, '사용자가 loginCookie를 가지고 있다면, 그 값은 과거에 로그인한 시점에 세션 아이디'라는 것입니다.
이를 반대로 해석해보면 loginCookie에 있는 값을 이용해서 데이터베이스에서 UserVO의 정보를 읽어오고, 읽어온 UserVO 객체를 현재의
HttpSession에 보관하면 로그인이 된다는 것입니다. 이 과정은 다음과 같은 순서로 이뤄지게 됩니다.
- 사용자가 로그인하면 데이터베이스에 현재 세션의 ID 값과 유효기관(7일) 을 기록합니다.
- 사용자가 로그인하지 않은 상태에서 쿠키를 가지고 접속하면 쿠키의 내용물을 추출합니다.
- 쿠키의 내용물로 데이터베이스를 조회해서 유효기간에 맞는 값인지 확인합니다.
- 확인된 사용자는 세션에 로그인한 정보를 기록해서, 자동으로 로그인이 되도록 합니다.
4.2.1 데이터베이스의 변경
이를 위해서 가장 먼저 데이터베이스에 tbl_user 테이블 변경합니다.
테이블 변경
alter table tbl_user add COLUMN
sessionkey VARCHAR (50) not null default 'none';
alter table tbl_user add column sessionlimit timestamp;
추가된 칼럼은 로그인하는 시점의 세션 아이디를 보관하는 sessionKey 칼럼과 sessionlimit 이름의 유효시간을 기록하는 칼럼입니다.
sessionlimit 칼럼의 경우 서버에서 다시 한 번 유효한 기간에 다시 접속을 했는지를 판단하기 위해서 작성해 주었습니다.
4.2.2 코드의 변경
사용자가 로그인을 하면 위의 칼럼들이 업데이트 돼야 하므로, 해당 SQL 을 작성해야 합니다.
4.2.2.1 UserDAO 의 변경
UserDAO에는 로그인한 사용자의 sessionKey와 sessionLimit를 업데이트하는 기능과, loginCookie에 기록된 값으로 사용자의 정보를
조회하는 기능을 추가했습니다.
LoginDTO
public class LoginDTO {
private String uid;
private String upw;
private boolean useCookie;
...이하 getter/setter, toString()
}
UserDAO
public interface UserDAO{
public UserVO login(LoginDTO dto) throws Exception;
public void keepLogin(String uid, String sessionId, Date next);
public UserVO checkUserWithSessionKey(String value);
}
UserMapper.xml
<update id="keepLogin">
update tbl_user set sessionKey=#{sessionId}, sessionLimit=#{next} where uid=#{uid}
</update>
<select id="checkUserWithSessionKey" resultType="UserVO">
select * from tbl_user where sessionKey =#{value} and sessionlimit > now()
</select>
UserDAOImpl
@Override
public void keepLogin(String uid, String sessionId, Date next){
Map<String, Object> paramMap =new HashMap<String, Object>();
paramMap.put("uid", uid);
paramMap.put("sessionId", sessionId);
paramMap.put("next", next);
session.update(namespace+".keepLogin", paramMap);
}
@Override
public UserVO checkUserWithSessionKey(String value){
return session.selectOne(namespace+".checkUserWithSessionKey", value);
}
4.2.2.2 UserService 의 변경
UserService에는 아래와 같이 로그인 정보를 유지하는 keepLogin과 과거에 접속한 사용자인지를 확인하는 기능을 작성합니다.
UserService
public interface UserService{
public UserVO login(LoginDTO dto) throws Exception;
public void keepLogin(String uid, String sessionId, Date next ) throws Exception;
public UserVO checkLoginBefore(String value);
}
UserServiceImpl
@Override
public void keepLogin(String uid, String sessionId, Date next) throws Exception{
dao.keepLogin(uid, sessionId, next);
}
@Override
public UserVO checkLoginBefore(String value){
return dao.checkUserWithSessionKey(value);
}
4.2.3 UserService 의 변경
UserService에는 아래와 같이 로그인 정보를 유지하는 keepLogin과 과거에 접속한 사용자인지를 확인하는 기능을 작성합니다.
UserService
public interface UserService {
public UserVO login(LoginDTO dto) throws Exception;
public void keepLogin(String uid, String sessionId, Date next) throws Exception;
public UserVO checkLoginBefore(String value);
}
UserServiceImpl
@Override
public void keepLogin(String uid, String sessionId, Date next)
throws Exception{
dao.keepLogin(uid, sessionId, next);
}
@Override
public UserVO checkLoginBefore(String value){
return dao.checkUserWithSessionKey(value);
}
4.2.3 UserController 의 변경
UserController 는 사용자가 '자동 로그인' 을 선택한 경우 필요한 기능르 추가합니다.
@RequestMapping(value="/loginPost", method=RequestMethod.POST)
public void loginPOST(LoginDTO dto, HttpSession session, Model model) throws
Exception{
UserVO vo =service.login(dto);
if(vo==null){ return ;}
model.addAttribute("userVO", vo);
if(dto.isUseCookie()){
int amount =60*60*24*7;
Date sessionLimit =new Date(System.currentTimeMillis()+(1000*amount));
service.keepLogin(vo.getUid(), seesion.getId(), sessionLimit);
}
}
코드이 핵심은 loginCookie 값이 유지되는 시간 정보를 데이터베이스에 저장하는 것입니다.
4.2.4 AuthInterceptor 의 변경
AuthInterceptor 에서는 현재 사용자의 세션에 login 이 존재하지 않지만, 쿠키 중에서 loginCookie 가 존재할 때 처리가 진행됩니다.
public class AuthInterceptor extends HandlerInterceptorAdapter{
private static final Logger logger=LoggerFactory.getLogger(AuthInterceptor.class);
@Inject
private UserService service;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
HttpSession session =request.getSession();
if(session.getAttribute("login")==null){
logger.info("current user is not logined");
saveDest(request);
Cookie loginCookie=WebUtils.getCookie(request, "loginCookie");
if(loginCookie !=null){
UserVO userVO =service.checkLoginBefore(loginCookie.getValue());
logger.info("USERVO :" +userVO);
if(userVO !=null){
session.setAttribute("login", userVO);
return true;
}
}
response.sendRedirect("/user/login");
return false;
}
return true;
}
}
AuthInterceptor 는 UserService 타입의 객체를 주입받습니다.
현재 사용자가 HttpSession 에 적당한 값이 없는 경우 loginCookie 를 가지고 있는지를 체크합니다.
만일 과거에 보관한 쿠키가 있다면 UserService 객체를 이용해서 사용자의 정보가 존재하는지를 확인합니다. 만일 사용자의 정보가 존재한다면
HttpSession 에 다시 사용자의 정보를 넣어주게 됩니다.
4.2.5 자동 로그인 테스트
위의 코드가 정상적으로 동작하는지 알아보기 위해서는 다음과 같은 테스트가 진행돼야 합니다.
- 로그인하지 않은 상용자가 로그인이 필요한 URI 에 접근하는 경우 : 로그인 페이지로 이동
- 로그인할 'Remember Me'를 체크하지 않은 상태에서 로그인 실행, 이후 로그인 필요한 페이지로 이동 : 정상적인 이동
- 브라우저를 종료하고, 2)번 테스트를 다시 진행 : HttpSession 이 변경됐으므로, 로그인 페이지로 이동
- 로그인화면에서 'Remember Me'를 선택하고 로그인
- 브라우저를 종료하고 다시 실행한 후 로그인이 필요한 URI 접속 : 정상적인 이동
게시물 등록의 경우 로그인이 필요한 URI 이므로 이 경로를 이용해서 테스트를 진행했을때 다음과 같이 결과가 처리됩니다.
댓글 ( 4)
댓글 남기기