시나리오
- user사용자가 form 로그인 하며 remember me 체크 박스 체크에 체크를 하고 로그인을 수행함
- 브라우저를 완전히 종료하고 다시 웹 페이지에 접근
사용 맥락
리멤버미 인증은 리멤버미 서비스를 통해 이루어집니다.
리멤버 미 서비스를 이해하기 전 리멤버 미 서비스가 사용되는 맥락을 먼저 이해 해 보겠습니다.
- form 로그인은
UsernamePasswordAuthenticationFilter
를 통해 이루어 진다.
- RememberMe 서비스는 명시적으로 rememberMeServices 를 등록하지 않을 경우
RememberMeConfigurer
에 의해 자동으로 설정되는 구현체인TokenBasedRemberMeServices
를 사용한다.
1. user사용자가 remember-me 체크 박스 체크에 체크를 하고 로그인을 수행함
UsernamePasswordAuthenticationFilter
의 추상체 AbstractAuthenticationProcessingFilter
에는 템플릿 메서드 패턴으로 대략적인 인증 절차가 구현되어 있습니다.- 구체적인 인증은 추상 메서드
attemptAuthentication
에 위임하고
- 성공/실패에 따른 후처리는
AuthenticationSuccessHandler
,AuthenticationFailureHandler
에 위임합니다.
- 추상체는 이를 위한 Template를 담당합니다.
if(인증 절차가 아니면){ 다음 필터로 고고 } else { 인증결과 = 인증 시도(요청, 응답) 인증 결과 가 null 이면 FilterChainProxy로 돌아가서 다음 필더 ㄱㄱ 인증 성공 처리( ); 중간에 예외 발생 했으면 실패 처리( ); }
AbstractAuthenticationProcessingFilter. doFilter
슈도 코드SecurityContextHolder 에 인증 결과 저장 리멤버미서비스.로그인성공(); 로그인성공핸들러.성공후처리();
AbstractAuthenticationProcessingFilter.successfulAuthentication
슈도 코드SecurityContextHolder 초기화 리멤버미서비스.로그인실패(); 로그인실패핸들러.실패후처리();
AbstractAuthenticationProcessingFilter.unsuccessfulAuthentication
슈도 코드맥락 - 로그인 과정에서 성공/실패에 따른 리멤머비서비스의 처리가 수행됩니다.
2. 브라우저를 완전히 종료하고 다시 웹 페이지에 접근
RememberMeAuthenticationFilter
가 동작합니다.if(이미 인증 객체가 있으면?){ 다음 필터로 고고 }else{ 리멤버미인증객체 = 리멤버미서비스.자동로그인(); if(리멤버미인증객체 != null){ 리멤버미인증객체 = 인증매니저.인증(리멤버미인증객체) 인증 성공 후처리 ... 중간에 예외 발생 했으면 실패 처리( ); } 다음 필터로 고고 }
RememberMeAuthenticationFilter.doFilter
의 슈도 코드- 구체적인 리멤버미 자동로그인 처리는 은 추상 메서드
RememberMeServices
에 위임하고
- 성공/실패에 따른 후처리는
AuthenticationSuccessHandler
,AuthenticationFailureHandler
에 위임합니다.
같은 인증 처리 필터 이다 보니 필터의 처리 과정이
AbstractAuthenticationProcessingFilter
와 매우 유사하다는 점이 흥미롭습니다.맥락 - 인증되지 않은 사용자가 요청을 수행할 경우 RememberMe 필터에 의해 리멤버미서비스의 자동 로그인 메서드가 호출됩니다.
RememberMeServices
리멤버미서비스는 꽤 단순한 인터페이스 입니다.
아래 세가지 메서드가 리멤버미 서비스 인터페이스 스펙의 전부이며 이들이 사용되는 모든 맥락을 위에서 살펴 보았습니다.
public interface RememberMeServices { 인증객체 자동로그인(HttpServletRequest request, HttpServletResponse response); void 로그인실패(HttpServletRequest request, HttpServletResponse response); void 로그인성공(HttpServletRequest request, HttpServletResponse response, 인증객체 성공인증객체); }
이를 바탕으로 리멤버미서비스의 구현체중 하나인
TokenBasedRemberMeServices
의 구현을 살펴보겠습니다.TokenBasedRememberMeServices
토큰 기반 리멤버미 서비스에서도 역시 추상체 AbstractRememberMeServices 를 바탕으로 추상화를 통해 많은 로직이 분리되어있습니다.
토큰 기반 리멤버미 서비스에서도 사용되는 맥락의 흐름의 순서대로 구현 메서드 들을 살펴 보겠습니다.
로그인 성공
리멤버미 요청이 아니라면 리턴 성공인증객체에서 사용자이름, 비밀번호 추출 setCookie(new String[] { 사용자이름, 만료시간, 토큰 시그니쳐 값 (만료시간, 사용자 이름, 비밀번호의 해시 값) })
토큰 기반 리멤버미 서비스.loginSuccess
슈도 코드String 배열을 ":" delimeter 로 연결하여 Base64 인코딩한 문자열을 remember-me라는 이름으로 HttpServletResponse 의 쿠키에 담는다
토큰 기반 리멤버미 서비스.setCookie
슈도 코드로그인 성공 시에는 응답의 쿠키에 “remember-me” 라는 이름으로 사용자이름과 만료시간, 토큰 시그니쳐를 포함한 내용을 포함시킵니다.
로그인 실패
//Sets a "cancel cookie" (with maxAge = 0) on the response //to disable persistent logins. cancelCookie();
토큰 기반 리멤버미 서비스.loginFail
슈도 코드자동 로그인
if(쿠기 없으면?){ return null }else{ String[] 쿠키토큰 = 쿠키를 디코딩하기 사용자 = 자동로그인쿠키처리(쿠키토큰); return 리멤버미인증토큰(사용자); }
쿠키 토큰의 길이가 3이 아니면 예외 빵 토큰의 만료시간이 넘었으면 예외 빵 userDetails = 쿠키의 사용자 이름으로 사용자 조회 예상되는토큰시그니처 = (만료시간, 사용자 이름, 비밀번호의 해시 값) 토큰 시그니처가 다르면 예외 빵 return userDetails
토큰 기반 리멤버미 서비스.
processAutoLoginCookie 슈도 코드
리멤버 미의 자동 로그인 은 (리멤버미)인증 객체를 생성해주는 것 까지 담당한다.