인프런 커뮤니티 질문&답변

고래돌님의 프로필 이미지

작성한 질문수

코드로 배우는 React with 스프링부트 API서버

JWT와 @PreAuthorize 이용하기

AccessDeniedHandler가 작동하지않습니다

작성

·

80

·

수정됨

0

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

    try {

        생략...
 
        SecurityContextHolder.getContextHolderStrategy().getContext().setAuthentication(token);

        filterChain.doFilter(request, response);

    } catch (Exception e) {

        log.info("---------------JWT ERROR!------------");
        log.info(e);
        log.info(e.getMessage());

        ObjectMapper mapper = new ObjectMapper();
        String jsonStr = mapper.writeValueAsString(Map.of("error", "ERROR_ACCESS_TOKEN"));

        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");

        PrintWriter writer = response.getWriter();
        writer.println(jsonStr);
        writer.close();

    }

}

강의의 JWT와 @PreAuthorize 이용하기 파트 후반부에 강의처럼 USER권한만 가진 사용자로 ADMIN권한이 필요한 메소드에 접근하니 포스트맨에서 AccessDenied exception이 뜨지않고
catch (Exception e)가 예외를 잡아버려서

 

{
    "error": "ERROR_ACCESS_TOKEN"
}

결과가 이렇게 나옵니다 이미 AccessDenied 핸들러도 시큐리티에 등록 시킨후 입니다
ADMIN권한을 가진 사용자 토큰으로 요청을 보내면 원하는 데이터도 잘 나옵니다


catch로 잡아서 예외를 던질까 시도해봤는데 이유는 모르겠지만 Exception에서만 예외가 잡힙니다ㅠ

그래서 여러가지 해결책을 알아봤는데
첫번째

catch (RuntimeException e) {

    log.info("---------------JWT ERROR!------------");
    log.info(e);
    log.info(e.getMessage());

    ObjectMapper mapper = new ObjectMapper();
    String jsonStr = mapper.writeValueAsString(Map.of("error", "ERROR_ACCESS_TOKEN"));

    response.setContentType("application/json");
    response.setCharacterEncoding("UTF-8");

    PrintWriter writer = response.getWriter();
    writer.println(jsonStr);
    writer.close();

}

이렇게 RuntimeException으로하면

{
    "error": "ERROR_ACCESSDENIED"
}

AccessDenied 핸들러가 잘 작동하구요

두번째

SecurityContextHolder.getContextHolderStrategy().getContext().setAuthentication(token);
    
} catch (Exception e) {

    log.info("---------------JWT ERROR!------------");
    log.info(e);
    log.info(e.getMessage());

    ObjectMapper mapper = new ObjectMapper();
    String jsonStr = mapper.writeValueAsString(Map.of("error", "ERROR_ACCESS_TOKEN"));

    response.setContentType("application/json");
    response.setCharacterEncoding("UTF-8");

    PrintWriter writer = response.getWriter();
    writer.println(jsonStr);
    writer.close();

}

filterChain.doFilter(request, response);

doFilter를 try catch 밖에 두어도 AccessDenied 핸들러가 잘 작동합니다

세번째

@ExceptionHandler(AccessDeniedException.class)
public ResponseEntity<?> notAuthorized(AccessDeniedException e) {

    return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getMessage());
}

RestControllerAdvice로 하면 핸들러가 작동안하긴해도 예외처리가 가능했습니다

근데 문제는 왜 원래코드에서 영상처럼 작동을 안하는지 이유를 모르겠습니다 제가 빠트린부분이 있을까요?
스프링부트 버전도 똑같이 맞춰보기도 했습니다

답변 1

0

구멍가게코딩단님의 프로필 이미지
구멍가게코딩단
지식공유자

질문을 보고 다시 한번 소스 코드를 실행해서 결과를 확인했습니다만 문제가 발생하지 않았습니다.

 

예제 코드는 해당 섹션의 마지막쪽에 수업자료로 등록되어 있으니 다운로드 받아서 실행해 보실 수 있습니다.

 

Access Denied는 시큐리티에 관리되는 UserDetails를 통해서 처리되므로

UsernamePasswordAuthenticationToken authenticationToken
= new UsernamePasswordAuthenticationToken(memberDTO, pw, memberDTO.getAuthorities());

SecurityContextHolder.getContext().setAuthentication(authenticationToken);

authenticationToken의 값이 어떻게 나오는지 확인이 필요합니다.

 

 

말씀하신 문제는 작성하신 코드를 보면서 실행해 봐야 원인을 알 수 있을 듯 합니다.

 

코드를 https://drive.google.com/drive/folders/1ZLHRKaXx8Ou8kZNd1WE6qALxq_wJODXW?usp=drive_link

에 올려주시거나 코드를 접근할 수 있는 방법을 cookie_00@naver.com으로 알려주시면 살펴볼 수 있을듯 합니다.

 

고래돌님의 프로필 이미지
고래돌
질문자

답변감사합니다 프로젝트 파일 db username 과 password만 지우고 드라이브에 올렸습니다 UserDetails 구현은 CustomUserDetails에서 했습니다

구멍가게코딩단님의 프로필 이미지
구멍가게코딩단
지식공유자

JWTCheckFilter에서 예외를 잡아서 직접 던지시는게 제일 간단합니다. 강의와 다른 버전이라.. 좀 더 테스트가 필요하긴 하지만 우선 해결책으로.. 예외를 잡아서 던지시면 처리가 되실 겁니다.

 

 

 

    SecurityContextHolder.getContextHolderStrategy().getContext().setAuthentication(token);

    filterChain.doFilter(request, response);
}catch(ServletException e){

    throw e;

} catch (Exception e) {

    log.info("---------------JWT ERROR!------------");
    log.info(e);
    log.info(e.getMessage());

 

고래돌님의 프로필 이미지
고래돌
질문자

강의에서 사용한 스프링부트 버전이 3.1.5 버전이라 3.1.5로 다운그레이드도 해봤는데 동일한 결과가 타나나더라구요ㅠ
jjwt 버전이 좀더 최신이여서 문제가 되지는 않을거같은데...
해결책으로 주신방법은 AccessDenied 핸들러가 잘 작동합니다

근데 로그창에

image.png

이런 로그도 같이뜨네요..

구멍가게코딩단님의 프로필 이미지
구멍가게코딩단
지식공유자

강의 코드 역시 버전 업을 시키니 예외 발생 메시직가 동일하게 나옵니다.

다만 버전업을 시켜도 별도의 추가적인 예외 처리를 하지는 않았습니다.

 

개인적으로 작성하신 코드의 내부를 좀 더 살펴봐야 원인을 알 수 있을 듯 합니다.

고래돌님의 프로필 이미지
고래돌
질문자

넵 감사합니다 저도 열심히 찾아보는데 답이 안보이네요