public interface AuthenticationEventPublisher {
void publishAuthenticationSuccess(Authentication authentication);
void publishAuthenticationFailure(AuthenticationException exception, Authentication authentication);
}
@Component
public class CustomAuthenticationEventHandler {
private final Logger log = LoggerFactory.getLogger(getClass());
@Async
@EventListener
public void handleAuthenticationSuccessEvent(AuthenticationSuccessEvent event) throws InterruptedException {
//@Async, 쓰레드 분리 실습
Thread.sleep(5000l);
Authentication authentication = event.getAuthentication(); //인증이 성공적으로 완료된 사용자 객체
log.info("Successful authentication result: {}", authentication.getPrincipal());
}
@EventListener
public void handleAuthenticationFailureEvent(AbstractAuthenticationFailureEvent event) {
Exception e = event.getException();
Authentication authentication = event.getAuthentication();
log.warn("Unsuccessful authentication result: {}", authentication, e);
}
}
@EnableAsync //@Async 사용을 위해 설정
@Configuration
public class WebMvcConfigure implements WebMvcConfigurer {
}
Referrer 검증
CSRF Token 활용(권장)
CSRF 토큰의 사용: 로그인 완료 여부와 상관없이 사용자의 세션에 임의의 토큰 값을 저장하여, 모든 리소스 변경 요청(POST, PUT, DELETE 등)을 위한 요청마다 이 토큰 값을 요청 파라미터로 함께 전송
토큰 값의 일치 검증: 서버는 클라이언트로부터 받은 요청의 토큰 값과 사용자의 세션에 저장된 토큰 값이 일치하는지 검증
CsrfFilter는 요청이 리소스를 변경해야 하는 요청인지 확인하고, 맞다면 CSRF 토큰을 검증함 (기본적으로 활성화됨)
@GetMapping(path = "/asyncHello")
@ResponseBody
public Callable<String> asyncHello() {
log.info("[Before callable] asyncHello started.");
Callable<String> callable = () -> {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
User principal = authentication != null ? (User) authentication.getPrincipal() : null;
String name = principal != null ? principal.getUsername() : null;
log.info("[Inside callable] Hello {}", name);
return "Hello " + name;
};
log.info("[After callable] asyncHello completed.");
return callable;
}
@Async 어노테이션을 추가한 Service 레이어 메소드에는 해당 안되는 문제를 어떻게 해결할까?
@Bean
@Qualifier("myAsyncTaskExecutor")
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3); //풀 내 초기 스레드 수 설정
executor.setMaxPoolSize(5); //풀 내 허용되는 최대 스레드 수 설정
executor.setThreadNamePrefix("my-executor-"); //풀 내 스레드 이름에 붙을 접두사 설정
return executor;
}
@Bean
public DelegatingSecurityContextAsyncTaskExecutor taskExecutor(@Qualifier("myAsyncTaskExecutor") AsyncTaskExecutor delegate) {
return new DelegatingSecurityContextAsyncTaskExecutor(delegate);
}