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

모코코님의 프로필 이미지

작성한 질문수

스프링부트 시큐리티 & JWT 강의

안녕하세요 ! 질문이 있습니다.

24.02.26 16:41 작성

·

323

0

강사님의 강의를 통해 Jwt 와 시큐리티에 대한 공부를 수월히 할 수 있었습니다.

근데 개인적으로 이제 실습하면서 문제가 발생해서 자문을 구하고자 글을 쓰게 되었습니다.

 

로그인이 성공하면 토큰을 로컬 스토리지에 담고, 요청할 때는 토큰을 꺼내어 헤더에 담아서 검증절차를 진행하려고 합니다.

 

근데 분명 위의 프로세스에 해당 하는 로직을 작성하였다고 생각하였는데, 의도한 대로 흘러가지가 않아서 매우 난처한 상황입니다.

 

Index.html

<script>
    $(document).ready(function() {
        // 로그아웃 버튼 이벤트
        $('#logoutButton').click(function() {
            // 로컬 스토리지에서 토큰 제거
            localStorage.removeItem('accessToken');
            // 로그인 페이지로 리다이렉션
            window.location.href = '/login';
        });

        // 테스트 버튼 이벤트
        $('#testButton').click(function() {
            // 로컬 스토리지에서 토큰 가져오기
            const token = localStorage.getItem('accessToken');
            if (token) {
                $.ajax({
                    url: '/api/test', // 요청할 서버의 URL
                    type: 'GET', // HTTP 메서드
                    beforeSend: function(xhr) {
                        // 요청 헤더에 토큰 추가
                        xhr.setRequestHeader('Authorization', token);
                    },
                    success: function(data) {
                        // 요청 성공 시 로직
                        console.log("테스트 요청 성공:", data);
                        alert("테스트 요청 성공");
                    },
                    error: function(xhr, status, error) {
                        // 요청 실패 시 로직
                        console.error("테스트 요청 실패:", xhr.responseText);
                        alert("테스트 요청 실패");
                    }
                });
            } else {
                alert("토큰이 없습니다. 다시 로그인해주세요.");
            }
        });
    });
</script>

 

이와 같이 /url/test 로 api 요청을 할때 헤더에 토큰을 담게 했습니다.

이에 "테스트 버튼"을 누르게 되면

 

JwtAuthorizationFilter

@Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain filterChain) throws ServletException, IOException {
        // 헤더에서 토큰 추출
        log.info("헤더에서 토큰 추출");
        String tokenValue = jwtUtil.getJwtFromHeader(req);
        log.info("토큰 : " + tokenValue);

        if (StringUtils.hasText(tokenValue)) {

            // 토큰 유효성 검사
            if (!jwtUtil.validateToken(tokenValue)) {
                log.info("Token Error");

                return;
            }
            Claims info = jwtUtil.getUserInfoFromToken(tokenValue);

            try {
                setAuthentication(info.getSubject());
            } catch (Exception e) {
                log.error(e.getMessage());
                return;
            }
        }

        else {
            log.info("토큰이 없습니다.");
        }

        filterChain.doFilter(req, res);
    }

    // 인증 처리
    public void setAuthentication(String loginId) {
        log.info("인증 성공");
        SecurityContext context = SecurityContextHolder.createEmptyContext();
        Authentication authentication = createAuthentication(loginId);
        context.setAuthentication(authentication);

        SecurityContextHolder.setContext(context);
    }

    // 인증 객체 생성
    private Authentication createAuthentication(String loginId) {
        log.info("인증 객체 생성");
        UserDetails userDetails = userDetailsService.loadUserByUsername(loginId);
        return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
    }

 

로그로 토큰이 출력되는 것과 최종적으로 인증 객체 생성까지 되는 것을 확인했습니다.

그러나 이렇게 인가 필터를 거치고 이제 컨트롤러로 접근하게 되면

    @GetMapping("/api/test")
    public String testPage() {
        log.info("test controller");
        return "test";
    }

 

저기 test controller 라는 로그만 찍힌채 프로세스가 마무리됩니다.

 

최종로그

2024-02-26T16:19:36.394+09:00  INFO 60592 --- [nio-8081-exec-9] JWT 검증 및 인가                              : 토큰 : eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0NiIsImF1dGgiOiJPV05FUiIsImlhdCI6MTcwODkzMTk3MywiZXhwIjoxNzA4OTM1NTczfQ.A2AhGDg5phTcm4gGC-01K0jKoAGE1c5Ygsq9v_J1ntk
2024-02-26T16:19:36.426+09:00  INFO 60592 --- [nio-8081-exec-9] JWT 검증 및 인가                              : 인증 성공
2024-02-26T16:19:36.427+09:00  INFO 60592 --- [nio-8081-exec-9] JWT 검증 및 인가                              : 인증 객체 생성
Hibernate: 
    /* <criteria> */ select
        u1_0.id,
        u1_0.email,
        u1_0.login_id,
        u1_0.password,
        u1_0.role 
    from
        users u1_0 
    where
        u1_0.login_id=?
2024-02-26T16:19:36.434+09:00  INFO 60592 --- [nio-8081-exec-9] TestController                           : test controller

 

이때 웹 페이지의 개발자 도구를 열어서 콘솔로그를 확인 해보면, 제가 가지고 오고자 했던 test.html 의 코드만이 적혀있을 뿐입니다.

 

뭐가 문제인 걸까요..

 

혹시나 test 쪽에 토큰을 가져오는 로직도 작성해봤습니다.

 

Test.html

<head>
    <meta charset="UTF-8">
    <script src="https://code.jquery.com/jquery-3.7.0.min.js"
            integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous"></script>
    <script>
        $(document).ready(function () {
            const auth = getToken();

            if (auth) {
                $.ajaxPrefilter(function (options, originalOptions, jqXHR) {
                    jqXHR.setRequestHeader('Authorization', auth);
                });
            } else {
                window.location.href = '/user/login-page';
            }
        });

        function getToken() {
            let auth = localStorage.getItem('accessToken');
            return auth || '';
        }
    </script>
    <title>Test Page</title>
</head>
<body>
<h1>This is a test page.</h1>
</body>
</html>

 

그럼에도 여전히 해당 페이지에는 접근을 못하고 있습니다 ㅠㅠㅠㅠ

제가 놓친 부분이 뭐가 있을지 조언을 주신다면 감사하겠습니다.

답변 2

0

최주호님의 프로필 이미지
최주호
지식공유자

2024. 03. 04. 09:16

아래 콘솔 로그 확인 해보세요. Bearer 추가해주시고!!

imageimage

0

인프런 AI 인턴님의 프로필 이미지

2024. 02. 27. 16:46

안녕하세요, 인프런 AI 인턴입니다.
모코코님, 스프링 부트와 JWT를 활용한 보안 관련하여 실습 중 문제가 생겨 고민이 많으시군요. 문제를 해결하기 위해 몇 가지 가능성을 살펴보겠습니다.

  1. Authorization 헤더 형식 확인
  • JWT 토큰을 HTTP 헤더에 전달할 때 일반적으로 ‘Bearer’ 접두어를 붙여서 전송합니다. 현재 스크립트에서는 Token 값만 설정하고 있습니다. 형식이 Authorization: Bearer {token}과 같아야 한다는 점을 확인해 주세요.
  1. 서버의 CORS 설정 확인
  • 클라이언트와 서버가 다른 도메인에 있을 경우, 서버측에서 CORS(Cross-Origin Resource Sharing) 정책으로 인해 요청이 차단될 수 있습니다. 서버의 CORS 설정이 올바르게 되어 있는지 확인해야 합니다.
  1. 응답 처리 방식 확인
  • 컨트롤러에서 문자열을 반환할 때, test라는 문자열이 그대로 반환되고 있는 것으로 보입니다. 만약 test.html 페이지를 반환하고자 한다면 Thymeleaf나 JSP와 같은 템플릿 엔진을 사용하고 있지 않는다면, return "redirect:/test.html";과 같이 리다이렉트를 사용하거나 ModelAndView 객체를 사용하여 뷰 이름을 설정하는 방식을 사용해야 합니다.
  1. Security 설정 확인
  • 스프링 시큐리티 설정에서 인증된 사용자만 접근할 수 있는 URL 경로가 올바르게 설정되어 있는지, 인가 과정에서 문제가 없는지도 점검해야 합니다. 컨트롤러에 접근하기 전에 필요한 권한이 제대로 부여되었는지 확인해 보십시오.
  1. 브라우저 콘솔 및 네트워크 탭 확인
  • 브라우저의 개발자 도구에서 콘솔 로그뿐만 아니라 네트워크 탭을 통해 HTTP 요청과 응답에 대한 상세한 정보를 확인해보세요. 요청 헤더, 응답 상태 코드 및 응답 본문 등을 확인하면 문제를 파악하는 데 도움이 됩니다.

위의 가능성들을 하나씩 확인해가며 문제를 해결해보시기를 바랍니다. 추가적으로 필요하다면, 스프링 시큐리티의 테스트 관련 문서를 참고하실 수 있습니다. Spring Security Reference Documentation에서 더 자세한 정보를 얻으실 수 있습니다. 혹시나 설정이나 코드에 더 궁금한 점이 있다면 질문해 주세요.