본문 바로가기
Nodejs

[Nodejs] Express JWT 토큰 발급/검증

by hotdog7778 2023. 10. 19.

 - jsonwebtoken 패키지 설치

 - 발급 sign() 메서드 와 검증 verify() 메서드 를 이용해서 서버<->클라 간 토큰 발급 및 검증

 

 

1. 패키지 설치

npm i cookie-parser jsonwebtoken

 

 

2. 서버) 로그인 요청에 대해서 토큰 생성 (유저확인->토큰생성->응답)

// 로그인 처리 (토큰 생성)
const signIn = async (req, res) => {
  // 로그인 시도하는 유저 정보
  const { userId, userPw } = req.body;
  
  // .. ID/PW 확인 로직
  // 생략
  
  // 회원일때
  if (isMember) {
    // JWT 발급
    // sign({토큰의 내용}, 토큰의 비밀 키, {토큰의 설정}) , issuer 는 발급자임.
    const token = jwt.sign({ id: result[0].userid }, JWT_SECRET, { expiresIn: '10m', issuer: 'NodeServer' });
    console.log('컨트롤러 >> ', token);

    // 쿠키에 jwt를 담아서 보내보자
    res.cookie('jwtCookie', token, {
      maxAge: 30 * 60000,
      httpOnly: true,
    });
	
    // 요청 본문 (JWT를 쿠키로 보내도 되지만 요청 본문으로도 보낼 수 있다.)
    res.send({
      success: true,
      msg: '로그인 성공, 토큰 발행되었음',
      // token,
    });
  } else {
    res.send({
    success: false,
    msg: '로그인 실패',
    });
  }
};

두가지 방법으로 토큰을 클라이언트에게 전달해봄

 - 응답 헤더(header)에 토큰이 담긴 쿠키 정보를 포함

 - 응답 본문(body)에 토큰을 포함

 

 

3. 브라우저) 서버에게 토큰 검증요청

 

각각 방법으로 JWT를 전달 받았을때 클라이언트에서 처리방법도 다름.

 - 쿠키로 받았을때 : 다른 요청을 보낼 때 브라우저 쿠키에 있는 JWT를 자동으로 요청에 포함시켜서 보냄.

 - 본문로 받았을때 : 다른 요청을 보낼 때 헤더의 authorization에 JWT를 포함시켜서 요청

// 로그인 요청
axios({
  method: 'post',
  url: '/user/signin',
  data: {
    userId,
    userPw,
  },
}).then((response) => {
    //// 응답 쿠키로 JWT를 발급 받은 후
    // 서버에게 토큰 검증요청
    axios.get('/tokenTest')
      .then((res) => { console.log(res.data) })
      .catch((err) => { console.log(err) })

    //// 응답 본문으로 JWT를 발급 받은 후
    // 서버에게 토큰 검증요청
    const token = response.data.token;
	axios.get('/tokenTest', { headers: { authorization: token }, })
      .then((res) => { console.log(res.data) })
      .catch((err) => { console.log(err) })
});

 

 

4. 서버에서 토큰 검증 ('/tokenTest')

 

라우터

// 토큰 확인 라우터
router.get('/tokenTest', verifyToken, controller.tokenTest);

 

미들웨어 (verifyToken)

const jwt = require('jsonwebtoken');
const JWT_SECRET = 'jwtSecret';

// 토큰 검증 미들웨어
// 요청 헤더에서 토큰값을 확인 한후 비밀키로 검증한다.
exports.verifyToken = (req, res, next) => {
  try {
    // res.locals 에 저장한 내용은 다음 미들웨어로 전달된다.
    // verify(요청헤더에 저장된 토큰 , 비밀키)
    console.log('req.headers.authorization>>>>', req.headers.authorization);

    // res.locals.decoded에 저장하면 미들웨어 다음 컨트롤러에서 이 값을 사용할 수 있음.
    res.locals.decoded = jwt.verify(req.headers.authorization, JWT_SECRET);
    console.log('middleWare >> ', res.locals.decoded);
    
    return next();
  } catch (err) {
    // 여기서 검증 실패시 처리
    if (err.name === 'TokenExpiredError') {
      return res.status(419).json({
        msg: '토큰 만료',
      });
    }
    return res.status(401).json({
      msg: '유효하지 않는 토큰',
    });
  }
};

 

미들웨어를 거친 다음 컨트롤러에서 로직수행(tokenTest)

 - 응답으로 검증된 토큰을 리턴하는걸로 대체함

const tokenTest = (req, res) => {
  res.send(res.locals.decoded);
  // exp: 1697541810 // 토큰의 만료 시간
  // iat: 1697541210 // 토큰이 발급된 시간
  // id: "tgkim"  // 토큰 발급시 설정했던 유저의 id
  // iss: "NodeServer" // 토큰을 발급한 주체
};

 

 

5. 브라우저 쿠키에서 확인한 JWT

값이 헤더.페이로드.서명 형식으로 저장된다.

 - 헤더(HEADER): 토큰 종류와 해시 알고리즘 정보가 들어 있습니다.
 - 페이로드(PAYLOAD): 토큰의 내용물이 인코딩된 부분입니다.
 - 시그니처(SIGNATURE): 일련의 문자열로, 시그니처를 통해 토큰이 변조되었는지 여부를 확인할 수 있습니다.

 

 

 

토큰 위변조는 서버에서 검증을 진행하면 되지만... 검증된 JWT가 있는 쿠키를 다른 브라우저로 복사 붙여넣기하면 문제없는 JWT를 다른 사람이 사용할 수 있을것 같다.

>> IP를 사용해보기

1. JWT 내에 추가 정보 포함: JWT에 IP 주소와 사용자 에이전트 정보를 포함할 수 있습니다. 예를 들어, 사용자가 로그인할 때 현재 IP 주소와 사용자 에이전트 정보를 JWT에 추가합니다.

2. IP 주소 및 사용자 에이전트 비교: 각 요청을 처리하기 전에, JWT에 포함된 IP 주소 및 사용자 에이전트 정보를 현재 요청의 정보와 비교합니다. 일치하지 않는 경우, 보안 이슈로 간주할 수 있습니다.

3. 처리 또는 거부: IP 주소 또는 사용자 에이전트가 일치하지 않는 경우, 해당 요청을 거부하거나 추가 보안 검증을 요구