Hello

Spring Security 로그인 실패 시 에러메세지

by 볼빵빵오춘기

Toy Project를 진행하면서 로그인과 회원가입 기능을 Security로 구현했지만, 로그인 시 아무런 안내 없이 실패하면 사용자는 이유를 알 수 없어 혼란스러울 수 있다.
그래서 로그인에 실패할 경우, 사용자에게 안내 문구를 제공해 왜 로그인이 되지 않는지 명확히 알려주는 것이 필요하다고 생각하게 됐다.

 

SecurityConfig

@Bean public LoginSuccessHandler loginSuccessHandler(){
    return new LoginSuccessHandler();
}

@Bean
public LoginFailureHandler loginFailureHandler(){
    return  new LoginFailureHandler();
}

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{

    http.formLogin((formLogin) ->
        formLogin.loginPage("/loginForm").loginProcessingUrl("/login").successHandler(loginSuccessHandler()).failureHandler(loginFailureHandler()))
        .authorizeHttpRequests(authorize ->
            authorize.requestMatchers("/admin/**").hasAnyRole("ADMIN") // /center/** url => admin  권한이 있는 사용자만 접근 가능
        .anyRequest().permitAll() // 권한을 주지않은 url 이 아니면 접근 허용
    ).oauth2Login((oauth2) -> oauth2
        .loginPage("/loginForm").userInfoEndpoint(userInfoEndpoint -> userInfoEndpoint
        .userService(principalOauth2UserService)).successHandler(loginSuccessHandler()).failureHandler(loginFailureHandler()))
        .exceptionHandling(exceptionHandling -> exceptionHandling
        .authenticationEntryPoint(customAuthenticationEntryPoint()) // 인증되지 않은 사용자가 접근할 때 처리
        .accessDeniedHandler(customAccessDeniedHandler())); // 권한이 없는 사용자가 접근할 때 처리
    http.csrf(CsrfConfigurer::disable).cors(Customizer.withDefaults());

    return http.build();
}

 

※ 설정 코드 부분(더보기 참고)

더보기
@Bean public LoginSuccessHandler loginSuccessHandler(){
    return new LoginSuccessHandler();
}

@Bean
public LoginFailureHandler loginFailureHandler(){
    return  new LoginFailureHandler();
}

 

.userService(principalOauth2UserService)).successHandler(loginSuccessHandler()).failureHandler(loginFailureHandler()))

 

 

LoginSuccessHandler

로그인을 성공하면 SecurityContextHolder를 통해 인증된 사용자의 정보를 가져와 Authentication에 정보를 갖고있다.

@Component
public class LoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

    public LoginSuccessHandler() {
        // 로그인 성공 시 이동할 URL 설정
        this.setDefaultTargetUrl("/");
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        // SecurityContextHolder를 통해 현재 인증된 사용자의 정보를 가져옴
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        super.onAuthenticationSuccess(request, response, authentication);
    }
}

 

LoginFailureHandler

로그인 실패 시 어떤 Exception이 발생하는지 확인 후 그에 맞는 메세지를 출력하도록 한다.

@Configuration
public class LoginFailureHandler extends SimpleUrlAuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                        AuthenticationException exception) throws IOException, ServletException {

        String errorMessage = null;

        if (exception instanceof BadCredentialsException) {
            errorMessage = "아이디와 비밀번호를 확인해주세요.";
        } else if (exception instanceof InternalAuthenticationServiceException) {
            errorMessage = "내부 시스템 문제로 로그인할 수 없습니다. 관리자에게 문의하세요.";
        } else if (exception instanceof UsernameNotFoundException) {
            errorMessage = "존재하지 않는 계정입니다.";
        } else {
            errorMessage = "알 수없는 오류입니다.";
        }

        errorMessage = URLEncoder.encode(errorMessage, "UTF-8");
        setDefaultFailureUrl("/loginForm?error=" + errorMessage);
        super.onAuthenticationFailure(request, response, exception);
    }
}

 

실행 결과

 

 

블로그의 정보

Hello 춘기's world

볼빵빵오춘기

활동하기