Spring Security적용 후, 로그인 기능을 구현했다. 하지만 로그인 실패 시, 오류 메시지를 출력하려고 했다.
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
http.csrf().disable();
http.authorizeHttpRequests()
.requestMatchers(
new AntPathRequestMatcher("/question/modify/**")).authenticated()
.requestMatchers(
new AntPathRequestMatcher("/question/create**")).authenticated()
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/loginForm")
.loginProcessingUrl("/login")
.failureForwardUrl("/loginError")
.defaultSuccessUrl("/")
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/");
return http.build();
}
@PostMapping("/loginError")
public String loginerror(@Valid LoginForm loginForm, BindingResult bindingResult){
return "loginForm";
}
위 코드는 로그인 기능을 구현했을 때 코드이다.
로그인을 하기위해서/loginForm
경로로 이동하고, 로그인 처리를 하기 위해서/login
경로로 이동하도록 설정하였다.
따라서 올바른 정보를 입력하면defaultSuccessUrl
의 경로에 맞게 이동하는 걸 확인할 수 있었다.
그리고 잘못된 정보를 입력하면failureForwardUrl
의 경로에 맞게 이동해서@Valid
어노테이션을 이용해서 Username과 Password가 틀린 경우에 맞게 errorMessage를 출력할려고 했다.
위 방법을 구현하기 위해서 UserDetailsService를 구현한 CustomUserDetailsService 코드를 수정하였다.
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SiteUser user = siteUserRepository.findByUsername(username)
.orElseThrow(() -> {
throw new UsernameNotFoundException("해당 사용자를 찾을 수 없습니다.:"+username);
});
return new CustomDetails(user);
}
/login
경로 접근 시 호출되는loadUserByUsername
메소드에서 username에 해당하는 SiteUser객체가 없을 경우UsernameNotFoundException
이 발생하도록 작성하였다.
여기서부터 너무 많이 헤매었다... 아무리 재실행하여도 UsernameNotFoundException이 발생하지 않았다... 하지만 디버깅을 통해 확인해보면 orElseThrow가 작동하는 것을 확인했다..
그리고 많은 고민과 구글링을 통해서 해결방법을 찾게되었다...
참고자료1, 참고자료2를 보면서
failureHandler
메소드를 통해서 로그인 실패 시, 처리과정을 구분할 수 있었던 것이다... 즉, 로그인 실패 시,failureHandler
메소드의 처리과정 후failureForwardUrl
의 경로로 이동하는 것이다.
이전에는failureHandler
가 정의 되어있지 않아 일방적으로 실패 url로 이동하여 에러 처리 메시지를 보여주기 어려웠던 것이다.
@Bean
AuthenticationFailureHandler customAuthFailureHandler(){
return new CustomAuthFailureHandler();
}
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
String errorMessage = null;
if(exception instanceof BadCredentialsException || exception instanceof InternalAuthenticationServiceException){
errorMessage = "Username과 Password가 맞지 않습니다. 다시 확인해 주십시오";
}else if(exception instanceof DisabledException){
errorMessage = "계정이 비활성화 되었습니다. 관리자에게 문의하세요.";
}else if(exception instanceof CredentialsExpiredException){
errorMessage = "비밀번호 유효기간이 만료 되었습니다. 관리자에게 문의하세요.";
}else{
errorMessage = "알 수 없는 이유로 로그인에 실패하였습니다. 관리자에게 문의하세요.";
}
request.setAttribute("errorMessage", errorMessage);
request.getRequestDispatcher(DEFAULT_FAIL_URL).forward(request,response);
}