[Spring Security] 권한 체크와 오류 처리

spring_security

권한 체크와 오류 처리 #


스프링의 인증과 권한 #

authAuth


FilterSecurityInterceptor #

FilterSecurityInterceptor

public void invoke(FilterInvocation filterInvocation) throws IOException, ServletException {
	if (isApplied(filterInvocation) && this.observeOncePerRequest) {
		// filter already applied to this request and user wants us to observe
		// once-per-request handling, so don't re-do security checking
		filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
		return;
	}
	// first time this request being called, so perform security checking
	if (filterInvocation.getRequest() != null && this.observeOncePerRequest) {
		filterInvocation.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
	}
	InterceptorStatusToken token = super.beforeInvocation(filterInvocation); // (1)
	try {
		filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
	}
	finally {
		super.finallyInvocation(token); // (2)
	}
	super.afterInvocation(token, null); // (3)
}

ExceptionTranslationFilter #

이 필터는 FilterSecurityInterceptor 나 app 에서 올라오는 오류를 가로채 처리하는 작업을 한다.

ExceptionTranslationFilter


401 에러와 403 에러 #


AuthenticationEntryPoint #

void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException;

실습 #

(본 코드는 GitHub 페이지에서 확인할 수 있습니다.)

http
    ...
    .exceptionHandling(error->
        error
            .authenticationEntryPoint(authenticationEntryPoint)
    ...
@GetMapping("/login-required")
public String loginRequired(){
    return "loginRequired";
}

그리고 config 폴더에 AuthenticationEntryPoint 를 implement 한 CustomEntryPoint 를 구현한다.

public class CustomEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(
            HttpServletRequest request, 
            HttpServletResponse response,
            AuthenticationException authException) throws IOException, ServletException {
        
        request
            .getRequestDispatcher("/login-required")
            .forward(request, response);
    }
}

그렇게 구현한 CustomEntryPoint 클래스를 authenticationEntryPoint 에 넣어 설정한다.

http
    ...
    .exceptionHandling(error->
        error
            .authenticationEntryPoint(new CustomEntryPoint())
    ...

AccessDeniedHandler #

http
    .exceptionHandling(error->
        error
            // .accessDeniedPage("/access-denied")
            .accessDeniedHandler("")
public class CustomDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(
            HttpServletRequest request, 
            HttpServletResponse response,
            AccessDeniedException accessDeniedException) throws IOException, ServletException 
    {
        // do something...
    }
}
@PreAuthorize("hasAnyAuthority('ROLE_USER')")
@GetMapping("/user-page")
public String userPage() throws YouCannotAccessUserPage{
    if (true) {
        throw new YouCannotAccessUserPage();
    }
    return "UserPage";
}
public class YouCannotAccessUserPage extends AccessDeniedException {
    public YouCannotAccessUserPage() {
        super("유저페이지 접근 거부");
    }
}

다시 config의 CustomDeniedHandler 로 돌아와서,

public class CustomDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(
            HttpServletRequest request, 
            HttpServletResponse response,
            AccessDeniedException accessDeniedException) throws IOException, ServletException 
    {
        if (accessDeniedException instanceof YouCannotAccessUserPage) {
            request.getRequestDispatcher("/access-denied").forward(request, response);
        } else {
            request.getRequestDispatcher("/access-denied2").forward(request, response);
        }
    }
}

만약에 accessDeniedException 이 YouCannotAccessUserPage 클래스 라면 “/access-denied” 로 보내주고, 아니라면 “/access-denied2” 로 보내도록 설정한다.

protected void configure(HttpSecurity http) throws Exception {
    http
        ...
        .exceptionHandling(error->
            error
                .accessDeniedHandler(new CustomDeniedHandler())
        ...

이런식으로 에러페이지라고 하더라도 상황에 따라서 다른 에러페이지를 사용자에게 보여주려 한다면 이렇게 AccessDeniedHandler 를 핸들링 해야 할 수 있다.


AuthenticationException #