이 글은 정수원님의 Infrean 강의를 학습한 내용을 정리하여 작성합니다.
Ajax 인증 - AjaxAuthenticationProvider
- AuthenticationProvider 인터페이스 구현
- 인증 동작 조건
- supports(Class<?> authentication)
- ProvderManager로부터 넘어온 인증객체가 AjaxAuthenticationToken 타입이면 동작한다.
- supports(Class<?> authentication)
- 인증 검증이 완료되면 AjaxAuthenticationToken을 생성해 최종 인증 객체를 반환한다.
구현
Ajax용 AjaxSecurityConfig 생성
AjaxSecurityConfig
@Configuration
@Order(0)
public class AjaxSecurityConfig {
@Bean
public SecurityFilterChain ajaxFilterChain(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated()
);
http
.addFilterBefore(new AjaxLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
참고
AjaxSecurityConfig 클래스는 모든 URL 정보에 대해 동작하는 것이 아니라 특정 URL ("/api/**")에 대해서만 동작하도록 antMatcher() 함수를 다음과 같이 호출한다.
주의!
설정 클래스를 여러개 생성하는 경우 동작 순서를 지정해줘야 한다.
@Order 애노테이션 사용
AjaxAuthenticationProvider 생성
AjaxAuthenticationProvider
- Ajax 인증 방식 전용 AuthenticationProvider라 할지라도 Form 인증 방식과 별반 다를 게 없다.
- 이전에 구현했떤 Form 인증 방식용 FormAuthenticationProvider 코드를 사용한다.
- 다른점이 있다면 AuthenticationProvider에서 사용할 인증 객체가 UsernamePasswordAuthenticationToken이 아닌 우리가 생성한 Ajax 인증 방식용 인증 객체(AjaxAuthenticationToken)이라는 것이다.
public class AjaxAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String loginId = authentication.getName();
String password = (String)authentication.getCredentials();
AccountContext accountContext = (AccountContext) userDetailsService.loadUserByUsername(loginId);
// AuthenticationProvider는 사용자가 입력한 패스워드와 UserDetails의 패스워드와 비교한다.
if (!passwordEncoder.matches(password, accountContext.getPassword())) {
throw new BadCredentialsException("BadCredentialsException");
}
return new AjaxAuthenticationToken(accountContext.getAccount(), null, accountContext.getAuthorities());
}
@Override
public boolean supports(Class<?> authentication) {
return AjaxAuthenticationToken.class.isAssignableFrom(authentication);
}
}
- supports
- 전달받은 인증 객체가 AjaxAuthenticationToken 타입과 같을 때 동작한다.
설정 추가
- 위에서 생성한 AjaxAuthenticationProvider 객체를 설정 파일에 등록한다.
AjaxSecurityConfig
@Configuration
@Order(0)
public class AjaxSecurityConfig {
private AuthenticationConfiguration authenticationConfiguration;
@Bean
public AjaxAuthenticationProvider ajaxAuthenticationProvider() {
return new AjaxAuthenticationProvider();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
ProviderManager authenticationManager = (ProviderManager)authenticationConfiguration.getAuthenticationManager();
authenticationManager.getProviders().add(ajaxAuthenticationProvider());
return authenticationManager;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated()
);
http
.addFilterBefore(ajaxLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
http.csrf().disable();
return http.build();
}
@Bean
public AjaxLoginProcessingFilter ajaxLoginProcessingFilter() throws Exception {
AjaxLoginProcessingFilter ajaxLoginProcessingFilter = new AjaxLoginProcessingFilter();
ajaxLoginProcessingFilter.setAuthenticationManager(authenticationManager(authenticationConfiguration));
return ajaxLoginProcessingFilter;
}
}
실행
포스트맨 요청
- 스프링 시큐리티는 FilterChainProxy 클래스를 통해 여러 보안 필터들을 관리한다.
- 사용자 요청에 따라 그에 맞는 필터를 사용한다.
- 디버깅 모드를 통해 FilterChainProxy가 가지고 있는 필터를 확인해보면 우리가 등록한 AjaxLoginProcessingFilter가 있는 것을 확인할 수 있다.
AjaxLoginProcessingFilter 동작
- AjaxLoginProcessingFilter가 동작해 사용자의 username과 password 정보가 담긴 Ajax 인증 객체가 생성된다.
- 생성된 Ajax 인증 객체는 AuthenticationManager에게 전달된다.
AuthenticationManager 동작
- AuthenticationManager 인터페이스 구현체인 ProviderManager 클래스가 동작하게 되고 autheticate() 함수가 호출된다.
- 이때, 우리가 등록한 AjaxAuthentiationProvider가 존재하는 것을 확인할 수 있다.
AjaxAuthenticationProvider 동작
- AjaxAuthenticationProvider가 동작하고 사용자의 username과 password를 정상적으로 받아온다.
- 그리고 UserDetailsService로부터 AccountContext 정보 또한 정상적으로 가져온다.
- AjaxAuthenticationProvider는 모든 인증 과정을 통과하면 새로운 AjaxAuthenticationToken을 생성해 다시 AuthenticationManager에게 반환한다.
- 최종적으로 인증 Filter에게 반환된다.
결과
- AjaxAuthenticationProvider에서 인증 처리를 수행하고 최종적으로 인증에 성공한 AjaxAuthenticationToken을 인증 필터에게 전달해 나머지 작업을 수행하고 인증 처리는 마무리된다.
- 최종적으로 인증에 성공하면 현재 successHandler에서는 루트(/) 페이지로 이동하도록 리다이렉트 되어 있다.
- 현재 우리는 REST를 사용한 비동기 Ajax 인증 방식을 사용하고 있어 리다이렉트 할 수 없다.
- 이러한 문제를 해결하기 위해 Ajax 전용 successHandler를 생성해야 한다.
'스프링 시큐리티 > 실전프로젝트 - 인증 프로세스 Ajax 인증 구현' 카테고리의 다른 글
AjaxCustomDSLs구현하기 (0) | 2023.02.14 |
---|---|
인증 및 인가 예외 처리 - AjaxLoginUrlAuthenticationEntryPoint, AjaxAccessDeniedHandler (0) | 2023.02.14 |
인증 핸들러 - AjaxAuthenticationSuccessHandler, AjaxAuthenticationFailureHandler (0) | 2023.02.14 |
인증 필터 - AjaxAuthenticationFilter (0) | 2023.02.13 |
Ajax 인증 - 흐름 및 개요 (0) | 2023.02.12 |