이 글은 정수원님의 Infrean 강의를 학습한 내용을 정리하여 작성합니다.
Ajax 인증 - AjaxAuthenticationSuccessHandler, AjaxAuthenticationFauilureHandler
- 현재 인증 성공 후 SuccessHandler로 Form 인증 방식의 핸들러가 동작한다.
- 루트(/) 페이지로 redirect한다.
- 그러나 현재 진행중인 Ajax 방식은 비동기 방식이므로 루트 페이지 같은 화면으로의 이동이 아니라 인증에 성공한 최종적인 결과값을 Http 바디에 담아 넘겨주어야 한다.
- 이러한 과정을 수행하기 위해 Ajax 방식용 SuccessHandler와 FailureHandler를 구현하도록 한다.
AjaxAuthenticationSuccessHandler
- AuthenticationSuccessHandler 인터페이스 구현
- Response Header 설정
- response.setStatus(HttpStatus.OK.value());
- response.setContentType(MediaType.APPLICATION_JSON_VALUE);
- JSON 형식으로 변환하여 인증 객체 리턴
- objectMapper.writeValue(response.getWriter(), ResponseBody.ok(userDto));
AjaxAuthenticationFailureHandler
- AuthenticationFailureHandler 인터페이스 구현
- Response Header 설정
- response.setStatus(HttpStatus.UNAUTHORIZED.value());
- response.setContentType(MediaType.APPLICATION_JSON_VALUE);
- JSON 형식으로 변환하여 오류 메시지 리턴
- objectMapper.writeValue(response.getWriter(), ResponseBody.error(message));
구현
AuthenticationSuccessHandler
AjaxAuthenticationSuccessHandler
public class AjaxAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
// 인증에 최종 성공한 account 객체
Account account = (Account) authentication.getPrincipal();
response.setStatus(HttpStatus.OK.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
objectMapper.writeValue(response.getWriter(), account);
}
}
- onAuthenticationSuccess() 메소드의 authentication 파라미터는 최종 인증에 성공한 인증 객체이다.
- authentication.getPrincipal()을 수행하면 최종 인증에 성공한 Account 객체를 얻을 수 있다.
참고
AjaxAuthenticationProvider 클래스에서 최종 인증에 성공한 Authentication 객체를 생성할 때,
principal 값으로 다음과 같이 Account 객체를 등록하였다.
AuthenticationFailureHandler
AjaxAuthenticationFailureHandler
public class AjaxAuthenticationFailureHandler implements AuthenticationFailureHandler {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
String errorMsg = "Invalid Username or Password";
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
if(exception instanceof BadCredentialsException) {
errorMsg = "Invalid Username or Password";
} else if(exception instanceof DisabledException) {
errorMsg = "Locked";
} else if(exception instanceof CredentialsExpiredException) {
errorMsg = "Expired password";
}
objectMapper.writeValue(response.getWriter(), errorMsg);
}
}
Handler 등록
AjaxSecurityConfig
@Configuration
@Order(0)
public class AjaxSecurityConfig {
...
@Bean
public AuthenticationSuccessHandler authenticationSuccessHandler() {
return new AjaxAuthenticationSuccessHandler();
}
@Bean
public AuthenticationFailureHandler authenticationFailureHandler() {
return new AjaxAuthenticationFailureHandler();
}
@Bean
public AjaxLoginProcessingFilter ajaxLoginProcessingFilter() throws Exception {
AjaxLoginProcessingFilter ajaxLoginProcessingFilter = new AjaxLoginProcessingFilter();
ajaxLoginProcessingFilter.setAuthenticationManager(authenticationManager(authenticationConfiguration));
ajaxLoginProcessingFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler());
ajaxLoginProcessingFilter.setAuthenticationFailureHandler(authenticationFailureHandler());
return ajaxLoginProcessingFilter;
}
...
}
실행 (성공)
포스트맨
동작
AjaxLoginProcessingFilter
- 포스트맨으로 인증을 요청하면 AjaxAuthenticationFilter가 사용자의 username과 password를 담은 Authentication 객체를 생성해 AuthenticationManager에게 반환한다.
ProviderManager
- 등록되어 있는 AjaxAuthenticationProvider의 authentication() 메소드가 호출된다.
- 이때 Authentication 객체는 조금 전 필터가 전달해준 것이다.
AjaxAuthenticationProvider
- AjaxAuthenticationProvider는 최종 인증에 성공한 Authentication 객체를 다시 AuthenticationManager에게 전달하고 최종적으로 다시 인증 필터가 해당 인증 객체를 받게 된다.
AbstractAuthenticationProcessingFilter
- 인증에 성공했다면 필턴는 successHandler를 호출한다.
- 이때 successHandler는 우리가 등록한 AjaxAuthenticationSuccessHandler인 것을 확인할 수 있다.
AjaxAuthenticationSuccessHandler
- 여기서 Authentication 객체는 AjaxAuthenticationProvider에서 반환한 최종 인증에 성공한 인증 객체임을 확인할 수 있다.
- 해당 Authentication 객체에는 Account 객체와 권한 정보가 저장되어 있다.
- 최종적으로 클라이언트에게 JSON 형태로 응답한다.
결과
실행 (실패)
포스트맨
- 잘못된 패스워드 값으로 서버에 인증 요청
동작
AjaxLoginProcessingFilter
ProviderManager
AjaxAuthenticationProvider
- 패스워드가 일치하지 않아 예외가 발생한다.
AbstractAuthenticationProcessingFilter
- AjaxAuthenticationFailureHandler가 발생시킨 예외는 인증 필터가 받게된다.
- unseccessfulAuthentication() 메소드가 호출된다.
- 이때 예외는 AjaxAuthenticationProvider의 BadCredentialsException이다.
- AjaxAuthenticationFailureHandler의 onAuthenticationFailure() 메소드가 호출된다.
AjaxAuthenticationFailureHandler
결과
정리
- Ajax 인증과 Form 인증은 비슷하게 인증 처리, 실패가 동작한다.
- 하지만 차이점이 존재한다.
- Ajax 인증 방식의 경우 비동기식으로 응답하므로 리다이렉션과 같이 화면으로 응답하는 것이 아닌 데이터를 HTTP 바디에 담아 JSON 형식으로 응답한다.
'스프링 시큐리티 > 실전프로젝트 - 인증 프로세스 Ajax 인증 구현' 카테고리의 다른 글
AjaxCustomDSLs구현하기 (0) | 2023.02.14 |
---|---|
인증 및 인가 예외 처리 - AjaxLoginUrlAuthenticationEntryPoint, AjaxAccessDeniedHandler (0) | 2023.02.14 |
인증 처리자 - AjaxAuthenticationProvider (0) | 2023.02.14 |
인증 필터 - AjaxAuthenticationFilter (0) | 2023.02.13 |
Ajax 인증 - 흐름 및 개요 (0) | 2023.02.12 |