Spring Security - RembmerMeAuthenticationFilter

목차

참고

RembmerMeAuthenticationFilter

  • Client 로부터 Request(요청) 이 오게 되면 Request 객체 CookieRemember Me Token 이 있는지 확인한다.
  • Token 을 이용해 인증을 진행한 후 SecurityContext 에 Authentication 객체를 저장한다.

RememberMeAuthenticationFilter.java

private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {

// SecurityContext 내 Authentication 객체가 있는지 확인 한다.
if (SecurityContextHolder.getContext().getAuthentication() != null) {
this.logger.debug(LogMessage
.of(() -> "SecurityContextHolder not populated with remember-me token, as it already contained: '"
+ SecurityContextHolder.getContext().getAuthentication() + "'"));
chain.doFilter(request, response);
return;
}

// Remember Me 인증을 진행한 후 Authentication 객체를 반환받는다.
Authentication rememberMeAuth = this.rememberMeServices.autoLogin(request, response);

if (rememberMeAuth != null) {
// Attempt authenticaton via AuthenticationManager
try {
// RememberMeAuthenticationToken 객체내 Key 값에 대한 Hash 비교를 통해 유효성 인증을 진행 후 객체를 그대로 반환받는다.
rememberMeAuth = this.authenticationManager.authenticate(rememberMeAuth);

// SecurityContextHolder 에 인증 받은 Authentication 객체를 저장한다.
SecurityContextHolder.getContext().setAuthentication(rememberMeAuth);

// 인증 성공 후 후작업을 진행한다.
onSuccessfulAuthentication(request, response, rememberMeAuth);
this.logger.debug(LogMessage.of(() -> "SecurityContextHolder populated with remember-me token: '"
+ SecurityContextHolder.getContext().getAuthentication() + "'"));
if (this.eventPublisher != null) {
this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
SecurityContextHolder.getContext().getAuthentication(), this.getClass()));
}
if (this.successHandler != null) {
this.successHandler.onAuthenticationSuccess(request, response, rememberMeAuth);
return;
}
} catch (AuthenticationException ex) {
this.logger.debug(LogMessage
.format("SecurityContextHolder not populated with remember-me token, as AuthenticationManager "
+ "rejected Authentication returned by RememberMeServices: '%s'; "
+ "invalidating remember-me token", rememberMeAuth),
ex);
this.rememberMeServices.loginFail(request, response);
onUnsuccessfulAuthentication(request, response, ex);
}
}
chain.doFilter(request, response);
}

AbstractRememberMeServices

  • Request 객체로 부터 Remember Me 정보를 가져온 후
  • Base64 로 인코딩 Remember Me 정보를 Decoding 한다
  • 디코딩 된 Remember Me 를 이용해 Login 을 진행한 후 UserDetails 객체를 반환받는다.
  • Key 와 UserDetails 객체를 이용해 RememberMeAuthenticationToken 객체를 생성한다.
@Override
public final Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
// Cookie 로부터 Remeber Me Token 정보를 가져온다.
String rememberMeCookie = extractRememberMeCookie(request);

if (rememberMeCookie == null) {
return null;
}

this.logger.debug("Remember-me cookie detected");

if (rememberMeCookie.length() == 0) {
this.logger.debug("Cookie was empty");
cancelCookie(request, response);
return null;
}

try {
// Base64 로 인코딩 Remember Me Token 정보를 Decoding 한다.
String[] cookieTokens = decodeCookie(rememberMeCookie);

// Token 을 이용해 Login 을 진행한 후 UserDetails 객체를 반환받는다.
UserDetails user = processAutoLoginCookie(cookieTokens, request, response);
this.userDetailsChecker.check(user);
this.logger.debug("Remember-me cookie accepted");

// UserDetails 를 이용해 RememberMeAuthenticationToken (Authentication) 를 생성 후 반환한다.
return createSuccessfulAuthentication(request, user);
} catch (CookieTheftException ex) {
cancelCookie(request, response);
throw ex;
} catch (UsernameNotFoundException ex) {
this.logger.debug("Remember-me login was valid but corresponding user not found.", ex);
} catch (InvalidCookieException ex) {
this.logger.debug("Invalid remember-me cookie: " + ex.getMessage());
} catch (AccountStatusException ex) {
this.logger.debug("Invalid UserDetails: " + ex.getMessage());
} catch (RememberMeAuthenticationException ex) {
this.logger.debug(ex.getMessage());
}
cancelCookie(request, response);
return null;
}

RememberMeAuthenticationProvider

  • RememberMeAuthenticationToken 의 유효성을 판단할 때 Key 에 대한 Hash 값 비교를 통해 확인한다.
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (!supports(authentication.getClass())) {
return null;
}
if (this.key.hashCode() != ((RememberMeAuthenticationToken) authentication).getKeyHash()) {
throw new BadCredentialsException(this.messages.getMessage("RememberMeAuthenticationProvider.incorrectKey",
"The presented RememberMeAuthenticationToken does not contain the expected key"));
}
return authentication;
}
Share