로그인 시스템을 구현했지만 사용자가 로그인을 성공했을 경우만 구현하였고, 실패했을 경우는 따로 구현하지 않아 사용자가 로그인을 실패해도 별다른 메시지도 없이 페이지가 리로드 되기만 하여 이 부분을 개선해주었습니다.
📝 1. SimpleUrlAuthenticationFailureHandler 클래스를 상속받는 커스텀 핸들러 구현
- 로그인 실패와 관련된 예외는 HttpSecurity의 failureHandler()를 추가해주어야 캐치할 수 있다.
- 해당 메서드는 AuthenticationFailureHandler 인터페이스 구현체를 파라미터로 받는다. 따라서 AuthenticationFailureHandler 인터페이스를 구현하는 클래스를 생성해야 하는데 여기서는 SimpleUrlAuthenticationFailureHandler를 상속받았다.
- 그 이유는 해당 클래스에서 제공하는 setDefaultFailureUrl() 메서드를 사용하기 위해서이다.
- 해당 메서드는 로그인 실패 시 요청되는 url을 지정할 수 있으며 이를 통해 Controller에서 객체를 받아 View 단으로 Model을 넘길 수 있다.
package com.cos.blog.handler;
@Component
public class UserLoginFailHandler 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("/auth/loginForm?error=true&exception=" + errorMessage);
super.onAuthenticationFailure(request, response, exception);
}
}
여기서 errorMessage를 굳이 인코딩한 이유는 이후에 출력시킬 에러 메시지가 쿼리스트링 형태로 전달되도록 하였는데 errorMessage가 한글일 경우 정상적으로 출력되지 않기 때문이다. 따라서 수동으로 인코딩 해주어야 한다.
📝 2. SecurityConfig 클래스 수정
위에서 구현한 UserLoginFailHandler 클래스를 사용하기 위해 SecurityConfig 클래스에서 이를 DI하고 failureHandler() 메서드로 예외처리 핸들러를 지정해준다.
package com.cos.blog.config;
@Configuration //빈등록 (IoC관리)
@EnableWebSecurity //security 필터 등록
@EnableGlobalMethodSecurity(prePostEnabled = true) //특정 주소로 접근하면 권한 및 인증을 미리 체크하겠다는 뜻
@RequiredArgsConstructor
public class SecurityConfig {
private final PrincipalDetailService principalDetailService;
private final AuthenticationFailureHandler userLoginFailHandler;
...
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/", "/auth/**", "/js/**", "/css/**", "/image/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/auth/loginForm")
.loginProcessingUrl("/auth/loginProc")
.failureHandler(userLoginFailHandler)
.defaultSuccessUrl("/");
return http.build();
}
}
📝 3. Controller 수정
다음으로 Controller에 구현되어 있는 로그인 요청 시 호출되는 메서드를 아래와 같이 수정해준다.
@GetMapping("/auth/loginForm")
public String loginForm(@RequestParam(value = "error", required = false) String error,
@RequestParam(value = "exception", required = false) String exception,
Model model) {
model.addAttribute("error", error);
model.addAttribute("exception", exception);
return "user/loginForm";
}
📝 4. View 수정
마지막으로 View 페이지에서 Model 객체를 받아 에러 발생 시 사용자에게 메시지를 발생시킬 수 있도록 구현한다.
<span>
<c:if test="${error}">
<p id="valid" class="alert alert-danger">${exception}</p>
</c:if>
</span>
📝 실행 결과
💡 알게 된 점
- SimpleUrlAuthenticationFailureHandler를 통해 로그인 핸들러 설정하는 방법
'🚗 Backend Toy Project > 스프링 부트 게시판' 카테고리의 다른 글
[스프링부트 게시판] 25. 회원가입시 validation 체크 (0) | 2022.06.30 |
---|---|
[스프링부트 게시판] 24. Remember Me 기능 구현 (0) | 2022.06.29 |
[스프링부트 게시판] 22. 개선 및 수정사항 (0) | 2022.06.25 |
[스프링부트 게시판] 21. 댓글 구현 (2) | 2022.05.17 |
[스프링부트 게시판] 20. 카카오 로그인 (0) | 2022.05.16 |