이 글은 정수원님의 Infrean 강의를 학습한 내용을 정리하여 작성합니다.
Form 인증 - CustomAuthenticationFailureHandler
- Form 인증에 실패하는 경우 FailureHandler가 동작한다.
- 실패 이후 후속작업을 직접 지정할 수 있다.
구현
CustomAuthenticationFailureHandler
@Component
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
String errorMessage = "Invalid Username or Password";
if (exception instanceof BadCredentialsException) {
errorMessage = "Invalid Username or Password";
} else if (exception instanceof InsufficientAuthenticationException) {
errorMessage = "Invalid Secret Key";
}
setDefaultFailureUrl("/login?error=true&exception=" + errorMessage);
super.onAuthenticationFailure(request, response, exception);
}
}
- onAuthenticationFailure 함수의 인자로 주어지는 AuthenticationException 객체는 AuthenticationProvider 또는 UserDetailsService에서 발생한 예외를 의미한다.
- 예) 사용자 아이디에 해당하는 계정이 존재하지 않는 경우
- 예) 패스워드가 일치하지 않는 경우
- 이러한 이유로 발생한 인증 예외를 인증 필터가 받아 FailureHandler를 호출해 실패 처리를 진행한다.
- DefaultFailureUrl로 로그인("/login") 페이지를 지정하는데 추가로 예외 메시지를 보내준다.
LoginController
@Controller
public class LoginController {
@GetMapping("/login")
public String login(@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/login/login";
}
...
}
login.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="layout/header::userHead"></head>
<body>
<div th:replace="layout/top::header"></div>
<div class="login-form d-flex justify-content-center">
<div class="col-sm-5" style="margin-top: 30px;">
<div class="panel">
<p>아이디와 비밀번호를 입력해주세요.</p>
</div>
<div th:if="${param.error}" class="form-group" >
<span th:text="${exception}" class="alert alert-danger"></span>
</div>
<form th:action="@{/login_proc}" class="form-signin" method="post">
<input type="hidden" th:value="secret" name="secret_key">
<div class="form-group">
<input type="text" class="form-control" name="username" placeholder="아이디" required="required" autofocus="autofocus">
</div>
<div class="form-group">
<input type="password" class="form-control" name="password" placeholder="비밀번호" required="required">
</div>
<button type="submit" class="btn btn-lg btn-primary btn-block">로그인</button>
</form>
</div>
</div>
</body>
</html>
SecurityConfig
- 생성한 CustomAuthenticationFilterHandler를 호출하도록 설정 파일에 등록한다.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
...
@Autowired
private AuthenticationFailureHandler customAuthenticationFailureHandler;
...
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
.antMatchers("/", "/users", "user/login/**", "/login*").permitAll()
.antMatchers("/mypage").hasRole("USER")
.antMatchers("/messages").hasRole("MANAGER")
.antMatchers("/config").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login_proc")
.authenticationDetailsSource(authenticationDetailsSource)
.defaultSuccessUrl("/")
.successHandler(customAuthenticationSuccessHandler)
.failureHandler(customAuthenticationFailureHandler)
.permitAll();
return http.build();
}
}
주의!
현재 DefaultFailureUrl로 "login?error=true&exception" + exception.getMassage() 를 작성하였다.
여기서 우리가 바라는 것은 /login 페이지로 이동할 때 error와 exception을 추가로 보내는 것이다.
하지만 스프링 시큐리티에서는 전체를 하나의 경로로 인식한다.
그래서 위의 경로에서 /login 경로로 접근 가능하도록 다음과 같이 설정해야 한다.
실행
패스워드를 잘못 입력한 경우
- 사용자가 패스워드를 잘못 입려한 경우 CustomAuthenticationProvider에서 다음 예외가 발생한다.
- 그리고 CustomAuthenticationFailureHandler에서 다음과 같이 동작한다.
- LoginController에 error와 exception 값이 전달된다.
- login 페이지로 리다이렉트된다.
'스프링 시큐리티 > 실전프로젝트 - 인증 프로세스 Form 인증 구현' 카테고리의 다른 글
Form 인증 - 인증 사용자 정보 구하기 (0) | 2023.02.12 |
---|---|
인증 거부 처리 - Access Denied (0) | 2023.02.12 |
인증 성공 핸들러: CustomAuthenticationSuccessHandler (0) | 2023.02.12 |
인증 부가 기능 - WebAuthenticationDetails, AuthenticationDetailsSource (0) | 2023.02.12 |
로그아웃 및 인증에 따른 화면 보안 처리 (0) | 2023.02.12 |