이 글은 정수원님의 Infrean 강의를 학습한 내용을 정리하여 작성합니다.
Form 인증 - Access Denied
- 사용자가 인증 성공 후 특정 자원에 접근하려 할 때 해당 자원에 대한 접근 권한이 없으면 인가 예외가 발생한다.
- 인가 예외는 인증 필터가 아닌 FilterSecurityInterceptor 필터가 받아 ExceptionTranslationFilter를 통해 처리한다.
- 이번에는 AccessDeniedHandler를 구현한 CustomAccessDeniedHandler를 작성해 인가 예외가 발생한 경우 처리하도록 한다.
구현
CustomAccessDeniedHandler
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
private String errorPage;
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
String deniedUrl = errorPage + "?exception=" + accessDeniedException.getMessage();
response.sendRedirect(deniedUrl);
}
public void setErrorPage(String errorPage) {
this.errorPage = errorPage;
}
}
- 이때 deniedUrl 경로로 "/denied"를 사용한다.
LoginController
@Controller
public class LoginController {
...
@GetMapping("/denied")
public String accessDenied(@RequestParam(value = "exception", required = false) String exception,
Model model) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Account account = (Account) authentication.getPrincipal();
model.addAttribute("username", account.getUsername());
model.addAttribute("exception", exception);
return "user/login/denied";
}
}
SecurityConfig
- 인가 예외가 발생하면 직접 만든 CustomAccessDeniedHandler가 동작하도록 설정 파일에 등록한다.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
...
@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();
http.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler())
return http.build();
}
@Bean
public AccessDeniedHandler accessDeniedHandler() {
CustomAccessDeniedHandler accessDeniedHandler = new CustomAccessDeniedHandler();
accessDeniedHandler.setErrorPage("/denied");
return accessDeniedHandler;
}
}
denied.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="container">
<div class="row align-items-start">
<nav class="col-md-2 d-none d-md-block bg-ligh sidebar">
<div class="sidebar-sticky">
<ul class="nav flex-column">
<li class="nav-item">
<div style="padding-top: 10px;" class="nav flex-column nav-pills" aria-orientation="vertical">
<a th:href="@{/}" style="maring:5px;" class="nav-link text-primary">대시보드</a>
<a th:href="@{/mypage}" style="maring:5px;" class="nav-link text-primary">마이페이지</a>
<a th:href="@{/message}" style="maring:5px;" class="nav-link text-primary">메시지</a>
<a th:href="@{/config}" style="maring:5px;" class="nav-link text-primary">설정</a>
</div>
</li>
</ul>
</div>
</nav>
<div style="padding-top: 50px;" class="col">
<div class="container text-center">
<h1><span th:text="${username}" class="alert alert-danger"></span>님은 접근권한이 없습니다.</h1>
<br>
<h3 th:text="${exception}"></h3>
</div>
</div>
</div>
</div>
<div th:replace="layout/footer::footer"></div>
</body>
</html>
실행
- 미리 생성해둔 user 객체가 접근권한이 없는 메시지 자원에 접근하고자 할 때 AccessDeniedHandler가 다음과 같이 동작한다.
결과
'스프링 시큐리티 > 실전프로젝트 - 인증 프로세스 Form 인증 구현' 카테고리의 다른 글
Form 인증 - 인증 사용자 정보 구하기 (0) | 2023.02.12 |
---|---|
인증 실패 핸들러: CustomAuthenticationFailureHandler (0) | 2023.02.12 |
인증 성공 핸들러: CustomAuthenticationSuccessHandler (0) | 2023.02.12 |
인증 부가 기능 - WebAuthenticationDetails, AuthenticationDetailsSource (0) | 2023.02.12 |
로그아웃 및 인증에 따른 화면 보안 처리 (0) | 2023.02.12 |