[Nodejs] 쿠키, 세션, jwt 로그인

천우산__ ㅣ 2023. 5. 6. 14:32

로그인 기능을 구현할 때, 일반적으로 쿠키 혹은 세션을 기반으로 구현한다.

 

쿠키는 클라이언트 측에 저장되는 데이터로, 이것을 서버 측으로 전달하여 인증 및 상태 유지가 가능하다.

 

세션은 서버 측의 데이터로, 클라이언트 측에서 요청을 보내면 해당 요청을 기반으로 클라이언트의 정보를 기억하고, 일정 기간 이후 만료된다.

 

위의 두 가지 방식으로 로그인을 구현하는 경우, 각각의 문제점이 존재하는데

 

쿠키 방식을 사용하는 경우, 보안의 문제가 발생한다.

 

1. 쿠키 데이터 발생 주체는 클라이언트이며 식별이 가능한 관계로 다른 컴퓨터로 로그인 한 경우, 해당 컴퓨터의 다른 이용자가 로그인 정보 식별을 할 수 있으며

 

2. 쿠키 만료 기간이 길게 설정된 경우, 로그아웃을 하더라도 쿠키가 유지될 수 있다는 문제점이 있다.

 

반면, 세션 기반 방식을 사용하는 경우에는 관리 측면에서 어려움을 겪을 수 있다.

 

1. 인증 요청을 진행한 사용자 전체 정보를 요청 시 마다 서버 데이터베이스에서 확인 후 전달하면, 이용자 수에 따라 서버 속도가 느려질 수 있다.

 

쿠키와 세션 기반 로그인 방식의 문제점을 해결하기 위해 다양한 방법의 로그인 방식이 존재하는데, 그 중 하나가 jwt(json web token) 기반 로그인 방식이다.

 

1. 사용자가 로그인 요청과 함께 쿠키에 담아서 보내면

 

2. 서버에서는 로그인 정보 일치 여부를 확인하고

 

3. 정보가 일치하는 경우 유저 정보를 쿠키에 담아 전달하는데,

     전달하는 쿠키 정보는 서버측에서 생성한 비밀 키를 기반으로 서명된 정보이다. (이 때 만료 기간 설정 가능)

 

4. 사용자측에서 로그인이 필요한 서비스를 이용하는 경우, 이용할 때 마다 쿠키 정보를 검사하며

     쿠키 정보가 변조되지 않았는지, 만료 기간 이내 요청을 받았는지 확인 후 문제 없는 경우 서비스를 처리해 준다.

 

정리하면, jwt 방법을 통해 사용자 인증에 서버 측 부하를 줄이면서, 쿠키 정보 변조 여부를 검사할 수 있다는 장점이 있다.

 

아래는 jwt 를 통한 로그인 요청을 진행하는 코드 예제. 예제 코드에서 인증된 쿠키 기한은 설정하지 않았다.

const express = require("express");
const jwt = require("jsonwebtoken"); // jwt를 사용하기 위한 라이브러리
const User = require("../schemas/user.js");
const router = express.Router();

router.post('/login', async(req, res) => {
    try {
        const { nickname, password } = req.body;
        const userCheck = await User.findOne({ nickname });

        if (userCheck.length === 0 || userCheck.password !== password) {
            return res.status(412).send({
                errorMessage: "닉네임 또는 패스워드를 확인해주세요"
            })
        }
		
        // 로그인 정보가 일치하면, 비밀 키를 통해 서명 진행
        const token = jwt.sign({ nickname: userCheck.nickname }, "keys");
        
        // 쿠키에 사용자 인증 데이터 추가
        res.cookie("authorization", `Bearer ${token}`)
        return res.status(200).json({ token });

    } catch (error) {
        return res.status(400).send({
            errorMessage: "로그인에 실패하였습니다."
        })
    }
})

module.exports = router;

로그인 성공 후 로그인이 필요한 서비스 ( 마이페이지 접근, 게시글 생성 등)를 이용할 때, 로그인 여부를 확인한 후

 

서비스 접근을 허락하는데, 이 때 발급한 쿠키 정보 확인을 통해 유효 여부를 알 수 있다.

const jwt = require("jsonwebtoken");
const User = require("../schemas/user.js");

module.exports = async(req, res, next) => {
    try {
        const { authorization } = req.cookies;
		
        // 로그인이 되어있지 않은 경우 = 6번 라인 쿠키 정보가 없음
        if (!authorization) {
            return res.status(403).send({
                errorMessage: "로그인이 필요한 기능입니다."
            })
        }
        
        const [tokenType, tokenValue] = authorization.split(" ");

        // 인증 실패 = 변조 가능성 있음
        if (tokenType !== 'Bearer') {
            res.clearCookie('Authorization');

            return res.status(403).send({
                errorMessage: "전달된 쿠키에서 오류가 발생했습니다."
            })
        }
		
        // 비밀 키로 인증 정보 확인 (로그인에 사용한 비밀 키와 같은 것으로 확인)
        const { nickname } = jwt.verify(tokenValue, "keys");
        const user = await User.findOne({ nickname });

        res.locals.user = user;
        next();

    } catch (error) {
        res.clearCookie('Authorization');
        return res.status(403).send({
            errorMessage: "로그인이 필요한 기능입니다."
        })
    }
}