ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring Boot] 2-5. 스프링 시큐리티, 로그인 실패시 메시지 출력
    프로젝트/도서 관리 시스템 2023. 7. 5. 14:10

     

     

    현재 프로젝트에서는 로그인을 실패하면 아무 메세지가 보이지 않는다. 메세지는 없지만 페이지는 재로딩 되기에 이게 요청이 넘어간건지 클라이언트 입장에서는 당혹스러운 상황이 된다. 따라서 어떤 이유로 로그인에 실패했는지 에러 메세지를 띄워야 된다 생각한다. 이번 포스팅에선 로그인 실패시 메시지를 출력하는 기능을 구현해보려 한다. 

     

     

    📑 SecurityConfig 설정

     

    필드 변수로 customFailureHandler 객체를 선언해주었다. 

     

    private final AuthenticationFailureHandler customFailureHandler;

     

     

    그다음 로그인 실패시 customFailureHandler 가 동작하게 설정한다. failureHandler(customFailureHandler)를 추가하였다.

     

     

    //login 설정
    http.formLogin()
            .loginPage("/login") // GET 요청 (login form을 보여줌)
            .loginProcessingUrl("/login") //POST 요청 (login 창에 입력한 데이터를 처리)
            .failureHandler(customFailureHandler)
            .usernameParameter("email") //login에 필요한 id 값을 email로 설정 (default는 username)
            .passwordParameter("password") //login에 필요한 password 값을 password
            .defaultSuccessUrl("/"); //login에 성공하면 /로 redirect

     

     

    📑 CustomFailureHandler클래스 생성

     

    package com.wish.library.security.config;
    
    import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
    import org.springframework.security.authentication.BadCredentialsException;
    import org.springframework.security.authentication.InternalAuthenticationServiceException;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.net.URLEncoder;
    
    @Component
    public class CustomFailureHandler extends SimpleUrlAuthenticationFailureHandler  {
    
        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                            AuthenticationException exception) throws IOException, ServletException {
    
            String errorMessage;
            if (exception instanceof BadCredentialsException) {
                errorMessage = "아이디 또는 비밀번호가 맞지 않습니다. 다시 확인해 주세요.";
            } else if (exception instanceof InternalAuthenticationServiceException) {
                errorMessage = "내부적으로 발생한 시스템 문제로 인해 요청을 처리할 수 없습니다. 관리자에게 문의하세요.";
            } else if (exception instanceof UsernameNotFoundException) {
                errorMessage = "계정이 존재하지 않습니다. 회원가입 진행 후 로그인 해주세요.";
            } else if (exception instanceof AuthenticationCredentialsNotFoundException) {
                errorMessage = "인증 요청이 거부되었습니다. 관리자에게 문의하세요.";
            } else {
                errorMessage = "알 수 없는 이유로 로그인에 실패하였습니다 관리자에게 문의하세요.";
            }
    
            errorMessage = URLEncoder.encode(errorMessage, "UTF-8");
            setDefaultFailureUrl("/login?error=true&exception=" + errorMessage);
    
            super.onAuthenticationFailure(request, response, exception);
        }
    
    
    }

     

    setDefaultFailureUrl을 프로젝트에서 로그인시 설정해둔 경로로 설정하고, 각 상화에 맞는 에러메시지들을 설정해준다. 

     

     

     

    📑 Controller 수정 

     

    package com.wish.library.security.controller;
    
    import com.wish.library.security.domain.MemberSaveForm;
    import com.wish.library.security.domain.UserDTO;
    import com.wish.library.security.service.CustomUserService;
    import com.wish.library.security.service.MemberService;
    import lombok.RequiredArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.*;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.support.RedirectAttributes;
    
    /**
     * GET 방식, 화면 조회시
     */
    @Controller
    @RequiredArgsConstructor
    @Slf4j
    public class UserController {
    
        private final CustomUserService customUserService;
        private final MemberService memberService;
    
        @GetMapping("/")
        public String home(Model model){
            String email = (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            if(!(email.equals("anonymousUser"))) {
                log.info("index page, security 조회 email, getPrincipal : " + email);
                UserDTO user = (UserDTO) customUserService.loadUserByUsername(email);
                user.setPassword(null);
                model.addAttribute("user", user);
            }
            return "index";
        }
    
        @GetMapping("/login")
        public String login(Model model,
                @RequestParam(value="error", required = false) String error,
                @RequestParam(value = "exception", required = false) String exception){
            model.addAttribute("error", error);
            model.addAttribute("exception", exception);
            return "login";
        }
    
    }

     

     

    /login 경로를 가지는 login 메서드에 인자로 error와 exception을 받는다. @RequestParam 어노테이션을 사용했으며 required = false로 설정해서 꼭 파라미터가 들어오지 않아도 된다고 설정을 한다. 

     

    model에 "error"와 "exception"으로 저장했으므로 jsp 화면단에서 사용할 수 있다.

     

     

    📑 login.jsp

     

    만약 error의 값이 있다면 exception을 출력해줘라는 html 코드를 작성한다.

     

    <span>
        <c:if test="${error}">
            <p id="valid" class="alert alert-danger">${exception}</p>
        </c:if>
    </span>

     

     

    📑 결과 화면 

     

     

     


    ✔참고 

    반응형

    댓글

Designed by Tistory.