Json Web Token을 이용한 로그인

JWT

Json Web Token을 이용한 로그인

Json Web Token을 이용해 로그인 기능을 구현하기로 했다.
Node.js, Vue.js, MongoDB 환경에서 구현했다.
토큰 기반 인증 방식은 쿠키를 사용하지 않아도 되서 쿠키로 인해 발생하는 취약점을 없앨 수 있다.
또, 서버의 상태를 저장하지 않고(Stateless), 토큰 자체로 필요한 모든 정보를 가지고 있어(self-contained) 별도의 인증서버가 필요하지 않는다.
토큰은 Header.Payload.Signature의 형태로 이루어진다.
헤더에는 토큰의 타입과 암호화 방법을 암호화한다.
페이로드에는 실제 사용되는 데이터인 유저 정보, 토큰 만료일 등을 암호화한다.
시그니처는 데이터 무결성과 변조 방지를 위한 서명으로 헤더의 인코딩 값과 페이로드의 인코딩 값을 합쳐서 비밀키로 해시 암호화한다.

동작방식

1. 클라이언트에서 서버로 로그인 요청을 보낸다.

2. 로그인에 이상이 없으면 서버에서  JWT 발행한다.

    static createtoken = async(id, name) => {
        try {
            const token = jwt.sign({ 
                'id' : id, // id와 name 정보를 담는다.
                'name' : name
            }, process.env.SECRET_KEY, { //.env파일에 설정해둔 키로 암호화한다.
                expiresIn: '5h' // 토큰의 유효시간을 5시간으로 설정한다.
            });
            return token; // 토큰을 건네준다.
        } catch(err) {
            err.message = 'Model -> createtoken err';
            throw err;
        }
    };
    
3. 발행한 JWT를 클라이언트로 전달 후 localStorage에 저장했다.

async login(context ,token) {
            const user = jwt.decode(token); //토큰을 복호화한다.
            axios.defaults.headers.common[
                'Authorization'
            ] = `Bearer ${token}`;
            context.commit('set_token', token); // 프론트에 정보저장
            context.commit('set_name', user.name);
            context.commit('set_id', user.id);
            localStorage.setItem('token',token); // 로컬에 정보저장
            localStorage.setItem('name',user.name);
            localStorage.setItem('id',user.id);
        }

4. 이후 로그인해야 가능한 기능에 대해서 HTTP 통신 간에 Header와 같이 토큰을 보내서
서버에서 토큰을 검증하여 JWT이 유효한지 판단하고 유효한 토큰이면 기능을 실행한다.

const authMiddleware = async(req, res) => {
    if(req.headers && req.headers.authorization) {
        const token = await req.headers.authorization.split(' ');
        if(token[0] === 'Bearer') { // 토큰이 있다.
            jwt.verify(token[1],process.env.SECRET_KEY, function(err) {
                if(!err) { // 토큰이 유효하다.
                    return res.status(200).json({result : 1});
                }
                else { // 토큰이 만료되었다.
                    logger.error('Token Error\n' + err);
                    return res.status(200).json({result : 2});
                }
            });
        }
    }
    else { // 로그인이 되지 않았다.
        return res.status(200).json({result : 3});
    }
};

5. 유효 시간이 만료된 토큰이면 localStorage에서 로그인 정보를 삭제하고 다시 로그인이 필요하다는 경고창을 띄워주었다.

async logout(context) {
            delete axios.defaults.headers.common['Authorization'];
            context.commit('set_token', null);
            context.commit('set_name', null);
            context.commit('set_id', null);
            localStorage.removeItem('token');
            localStorage.removeItem('name');
            localStorage.removeItem('id');
        }