Spring boot + Security
๋ฅผ ์ฌ์ฉํ๊ณ ์๋๋ฐ ์๋ฌ ์ฝ๋ ๋ง๋ค ํ์ด์ง๋ฅผ ๋ณด์ฌ์ฃผ๊ณ ์ถ์๋ฐ ์ด๋ป๊ฒ ํด์ผํ๋์ง ๋ฐฉ๋ฒ์ ์๊ฐํด๋ณด๋ค๊ฐ ๋๊ฐ์ง ๋ฐฉ๋ฒ์ด ์๊ฐ๋ฌ์ต๋๋ค.
1. EntryPoint, Handler ์ฌ์ฉ
authenticationEntryPoint
, accessDeniedHandler
์์ ์ฌ์ฉ์์๊ฒ ํด๋น ์๋ฌ ํ์ด์ง๋ก ๋ฆฌ๋ค์ด๋ ์
์ ์ํค๋ ๋ฐฉ๋ฒ์
๋๋ค.
Security Config
/* Security Config */
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// ..
.exceptionHandling()
.authenticationEntryPoint(new MyAuthenticationEntryPoint())
.accessDeniedHandler(new MyAccessDeniedHandler())
// ..
;
}
AuthenticationEntryPoint
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.sendRedirect("/unauthorized");
}
}
AccessDeniedHandler
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.sendRedirect("/access-denied");
}
}
Controller ์ฝ๋ ์ถ๊ฐ
public class MyErrorController {
@GetMapping("/unauthorized")
public String unauthorizedPage() {
return "/todoList/unauthorized";
}
@GetMapping("/access-denied")
public String accessDeniedPage() {
return "/todoList/access-denied";
}
}
๊ฐ ์๋ฌ ํ์ด์ง ์์ฑ (์๋ต)
ํด๋น ์๋ฌ ๋ด์ฉ์ ์ฌ์ฉ์์๊ฒ ํ์ํด์ค ์ ์๋ ํ์ด์ง๋ฅผ ๋ง๋ค์ด์ค๋๋ค.
2. Spring Security์์ ์ ๊ณตํ๋ ๊ธฐ๋ฅ ์ฌ์ฉ
์ฐพ์๋ณด๋๊น ์คํ๋ง ์ํ๋ฆฌํฐ์์ ErrorController
๋ฅผ ๊ตฌํํ๋ฉด ์๋ฌ ํ์ด์ง๋ง๋ค ์ผ์ผํ API๋ฅผ ์์ฑํ ํ์ ์์ด ํ๋์ API์์ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
@Controller
public class HomeController implements ErrorController {
/* ๊ตฌํ ํด์ผํ๋ ๋ฉ์๋ */
@Override
public String error() {
return "/error";
}
/* error๋ฅผ ๋ฐ์์ ์ฒ๋ฆฌ */
@GetMapping("/error")
public String error(HttpServletRequest request) {
Integer errorCode = (Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
/* Error Code ๋ณ๋ก ํ์ด์ง ์ ๊ณต */
switch (errorCode){
case 401:
return "/unauthorized";
case 403:
return "/access-denied";
case 404:
return "/not-found";
default:
return "/server-error";
}
}
}
์ด๋ป๊ฒ ๊ฐ๋ฅํ ๊น์?
์ Whitelabel Error Page๋ BasicErrorController๊ฐ ๋ฐ์์ ์ฒ๋ฆฌํ๊ฒ ๋ฉ๋๋ค.
ErrorMvcAutoConfiguration์ด๋ผ๋ ํด๋์ค์์ BasicErrorController๋ฅผ ๋น์ผ๋ก ๋ง๋๋๋ฐ ์ด๋ @ConditionalOnMissingBean์ด ๋ถ์ด์์ด์ ErrorController๊ฐ ์์ ๋ ๋น์ผ๋ก ๋ฑ๋ก์ ํ๊ณ ์์ต๋๋ค.
ํ์ง๋ง ์ฐ๋ฆฌ๋ ์์์ ErrorController๋ฅผ ๊ตฌํํ๊ธฐ ๋๋ฌธ์ BasicErrorController๊ฐ ๋น์ผ๋ก ๋ฑ๋ก์ด ์๋์๊ณ , ์ฐ๋ฆฌ๊ฐ ์ค์ ํ ์๋ฌ ํ์ด์ง๋ฅผ ๋ณผ ์ ์๋ ๊ฒ์ ๋๋ค.
@AutoConfiguration(before = WebMvcAutoConfiguration.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
@EnableConfigurationProperties({ ServerProperties.class, WebMvcProperties.class })
public class ErrorMvcAutoConfiguration {
// ์๋ต..
@Bean
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes,
ObjectProvider<ErrorViewResolver> errorViewResolvers) {
return new BasicErrorController(errorAttributes, this.serverProperties.getError(),
errorViewResolvers.orderedStream().collect(Collectors.toList()));
}
// ..
}
REFERENCES
- https://www.baeldung.com/spring-boot-custom-error-page
- https://jungguji.github.io/2020/04/15/custom-error-page/
- https://frontendshape.com/post/bootstrap-5-404-page-examples
- https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/