개발자
안녕하세요 JWT 토큰을 이용해 로그인 검증하는 로직을 구현하고 있습니다. 로컬스토리지에 토큰을 저장하는 것까지 성공했으나 프론트 측에서 로그인 검증을 요청할때 실제 토큰 값을 받아오지 못하는 문제가 있어 질문 드립니다. login-required에서 토큰 값 유무 로직을 전부 지우고 콘솔로만 찍는 경우 undefined와 실제 토큰값이 나타납니다. 만약 토큰 값 유무 로직을 이용한다면 undefined값만 서버로 들어와 페이지가 무한로딩 되는 현상이 나타납니다. next()처리가 안되기 때문인 것 같습니다. (+ index.html은 test.js를 연결하고 있습니다.) 사진으로 보면 네트워크 요청헤더에서 확인한 결과 1. 처음엔 document 유형의 localhost를, (인증 헤더 x) 이후 js를 불러온다 2. 마지막에 xhr형식인 localhost를 불러옵니다 (인증 헤더 o) (여기서 undefined, 실제 토큰값이 콘솔에 동시에 나타나는 이유라고 생각이 듦) (순서를 보면 document 유형 localhost -> css, js -> xhr 유형 localhost 즉 처음에 document 유형에서 인증헤더를 못가져와서 (js가 로드되기 전이여서) 토큰 유무를 체크하지 못하는 것 같습니다) 결론적으로 제가 생각한 문제 분석 (1) ('/') 에 get 요청을 보낸다. (2) 서버는 loginRequired를 실행한다. (3) 서버로 부터 html을 받지 못한 상태에 loginRequired가 실행되어 토큰 값은 undefined가 된다. (4) next()를 거쳐 서버에서 html을 클라이언트로 부터 응답을 해준다. (5) html을 브라우저에 보여주면서 그 html에 연결된 js를 불러온다. (6) 이때 불러온 test.js에서 get요청 ('/') 로직을 불러와 다시 server.js에서 loginRequired 응답으로 토큰 값을 불러온다. 즉 js가 로드가 되어야 userToken을 로컬스토리지로 부터 받아오고 요청을 하여 토큰 값을 불러올 수 있다. 그런데 get 요청전에 토큰 검증을 하고 허가가 되어야 html을 로드해야 하지 않을까? 그렇다면 서버에서 처리를 해야하는가? 배워가는 학부생으로 많이 부족하다고 생각합니다,, 선배님들의 견해를 받고 싶어 게시물 올립니다..! 핵심 코드는 다음과 같습니다.
1서버측
2
3(server.js)
4app.get('/', loginRequired, (req, res) => {
5 res.sendFile(__dirname + '/views/index.html');
6});
7
8(login-required.js)
9const jwt = require('jsonwebtoken');
10
11const loginRequired = async (req, res, next) => {
12 // TEST CODE
13 const userToken = req.headers["authorization"]?.split(' ')[1]; // request 헤더로 부터 authorization bearer 토큰 받음 (공부 필요)
14 console.log(userToken + ' TEST===1');
15
16 // if(userToken) { <= 이 코드로 실행시 무한로딩
17 // next();
18 // }
19 next() // undefined, 실제토큰 순으로 콘솔에 찍힘
20
21}
22
23module.exports = loginRequired;
24
25
26프론트측
27
28(test.js)
29const userToken = localStorage.getItem('token');
30if (userToken) {
31 const headers = {
32 authorization: `Bearer ${userToken}`,
33 };
34
35 axios.get('/', { headers, withCredentials: true })
36 .then(response => {
37 // console.log(response.data);
38 })
39 .catch(error => {
40 console.error(error);
41 });
42} else {
43 // 토큰 값이 없을때 구현해야 하는 로직
44}
답변 2
작동되는지 모르겠지만, 혹시 발상의 전환이 되지 않을까 싶어서 일단은 남겨볼게요. (server.js) app.get('/', loginRequired, (req, res) => { if res.status===200 res.sendFile(__dirname + '/views/index.html'); }); (login-required.js) const jwt = require('jsonwebtoken'); const loginRequired = async (req, res, next) => { try { 토큰취득, 디코딩 next(); } catch (err) { return res.status(401).json({ message: 'Not authenticated' }); } } - try catch문으로 토큰취득 실패시 인증실패 메세지 설정 근데 304 이라 콘솔에 cors에러나 다른에러 안뜨면 다행이네요
익명
작성자
2024년 04월 06일
재민님 안녕하세요 ! 좋은 답변 감사합니다. 위 방법도 사용해보겠습니다. 보다 먼저 AI 봇이 달아준 답변이 있어 원인을 알게 되었고 시도를 하니 해결하였습니다. 저와 같은 문제가 생길 수 있는 분도 있으니 이 글을 남겨두려고 합니다. 감사합니다 !
익명
작성자
2024년 04월 07일
ai 답변이 5일뒤에 다른 분들께 공개가 된다고 하네요 ! 제가 복붙해드리면 답변은 다음과 같습니다 당신의 코드를 살펴보면 문제는 서버가 '/'에 대한 요청을 두 번 받고 그 사이에 클라이언트 측 JavaScript가 실행되어 localStorage에서 토큰을 가져오는 것 같습니다. 이로 인해 첫 번째 요청에서는 토큰이 정의되지 않았으며, 다음 요청에서만 토큰이 존재하는 것입니다. 일반적으로 JWT authentication의 경우, 로그인 api 호출 후 서버가 token을 반환하고 이 token을 client side에서 저장합니다. 로그인 이외의 모든 secure routes 호출 시 request header 안에 Authorization 헤더와 함께 이 token을 보냅니다. 따라서 여기서 문제점은, 최초 접속 시 '/' 라우터가 html 파일을 반환하게 되며, 이 때 axios를 활용한 추가 GET 요청이 발생하여 JWT token이 server로 전달되는 구조입니다. 하지만 배포나 실제 상황을 고려한다면 굳이 같은 엔드포인트('/')를 2번 호출할 필요는 없습니다. 클라이언트 코드를 수정하여 서버로부터 HTML 페이지를 처음 로드할때 보안 토큰 없이 리소스를 가져오도록 하세요. 그런 다음 사용자가 나중에 인증된 작업(예: 보호된 API 엔드포인트에 대한 요청)을 수행하려고 할 때 토큰을 사용하세요. 이 방법은 HTML의 초기 로드를 블록하지 않으므로 페이지가 더 빨리 로드됩니다. 다음과 같은 구조를 추천합니다: 1. 클라이언트는 로그인 API를 호출하고 서버로부터 token을 받아와 localStorage에 저장한다. 2. 이후 보안이 필요한 API 호출 시에는 헤더에 해당 token을 담아서 요청한다. 3. 서버에서는 해당 API 요청이 들어올 때 마다 Authorization 헤더에 있는 token을 확인하여 인증한다. 따라서 프론트쪽에서 '/' 경로를 fetch 하는 axios.get('/') 를 없애도 됩니다. ```javascript //test.js를 이렇게 바꿉니다: const userToken = localStorage.getItem('token'); if (userToken) { const headers = { authorization: `Bearer ${userToken}`, }; axios.get('/secure-endpoint', { headers, withCredentials: true }) .then(response => { // console.log(response.data); }) .catch(error => { console.error(error); }); } else { // 토큰 값이 없을때 구현해야 하는 로직 } ``` 서버 측의 코드를 수정하여 '/secure-endpoint' 경로에서 JWT 검증을 하게 해 주세요: ```javascript //server.js app.get('/secure-endpoint', loginRequired, (req, res) => { res.json({data: "secure data"}); }); ``` 이렇게 하면 클라이언트쪽에서 페이지 로드 후 인증이 필요한 API를 호출하게 됩니다. 이때 JWT 토큰을 함께 보내므로 서버에서는 이를 검증할 수 있습니다.
지금 가입하면 모든 질문의 답변을 볼 수 있어요!
현직자들의 명쾌한 답변을 얻을 수 있어요.
이미 회원이신가요?
지금 가입하면 모든 질문의 답변을 볼 수 있어요!