πŸŒ— The Art of React part3

hwakyungChoiΒ·2021λ…„ 1μ›” 3일
0

⚑️ ν”„λ‘œμ νŠΈ 정리

  • JWT(JSON Web Token)을 μ΄μš©ν•˜μ—¬ μ„œλ²„μ— νšŒμ› 인증 μ‹œμŠ€ν…œμ„ κ΅¬ν˜„ν•΄λ³΄λŠ” ν”„λ‘œμ νŠΈλ₯Ό ν•΄λ³΄μ•˜μŠ΅λ‹ˆλ‹€.
  • μ„Έμ„  기반 인증 μ‹œμŠ€ν…œκ³Ό 토큰 기반 인증 μ‹œμŠ€ν…œ 쀑 토큰 기반 인증 μ‹œμŠ€ν…œμ„ μ‚¬μš©ν•˜λŠ”λ° 이 μ‹œμŠ€ν…œμ€ μ‚¬μš©μž 둜그인 정보λ₯Ό κΈ°μ–΅ν•˜λŠ”λ° ν•„μš”ν•œ λ¦¬μ†ŒμŠ€κ°€ μ λ‹€λŠ” μž₯점을 가지고 μžˆμŠ΅λ‹ˆλ‹€.
  • κ·Έ 외에 κ΅¬ν˜„μ˜ κ°„νŽΈμ„±κ³Ό μ‚¬μš©μžλ“€μ˜ 인증 μƒνƒœλ₯Ό κ΄€λ¦¬ν•˜κΈ° νŽΈν•˜λ‹€λŠ” μž₯점 λ˜ν•œ 가지고 μžˆμŠ΅λ‹ˆλ‹€.
  • JWT 토큰을 λ§Œλ“€κΈ° μœ„ν•˜μ—¬ jsonwebtoken λͺ¨λ“ˆ μ„€μΉ˜κ°€ ν•„μš”ν•©λ‹ˆλ‹€.
  • μ‚¬μš©μžκ°€ 토큰을 μ‚¬μš©ν•  λ•Œ 주둜 λΈŒλΌμš°μ €μ˜ localstorage λ˜λŠ” sessionstorage에 λ³΄κ΄€ν•˜κ±°λ‚˜ λΈŒλΌμš°μ €μ˜ 쿠킀에 λ‹΄μ•„μ„œ μ‚¬μš©ν•©λ‹ˆλ‹€.
  • μ „μžμ˜ 경우 XXS(Cross Site Scripting) 곡격에 μ·¨μ•½ν•˜λ©° ν›„μžμ˜ 경우, CSRF(Cross Site Request Forgery) 곡격에 μ·¨μ•½ν•©λ‹ˆλ‹€.
  • μ΄μƒμœΌλ‘œ λ°±μ—”λ“œ νŒŒνŠΈλŠ” λ§ˆλ¬΄λ¦¬λ˜μ—ˆμœΌλ©° λ¦¬μ•‘νŠΈλ₯Ό μ΄μš©ν•˜μ—¬ λΈ”λ‘œκ·Έ μ›Ή μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ λ§Œλ“€ μ˜ˆμ •μž…λ‹ˆλ‹€.

🎁 μ½”λ“œ

//auth.ctrl.js
require('dotenv').config();
import Joi from "joi";
import User from "../../models/user"

export const register = async ctx => {

    const schema = Joi.object().keys({
        username: Joi.string().alphanum().min(3).max(20).required(),
        password: Joi.string().required()
    })
    const result = schema.validate(ctx.request.body);
    if (result.error) {
        ctx.status = 400;
        ctx.body = result.error;
        return;

    }
    const { username, password } = ctx.request.body;

    try {
        const exists = await User.findByUsername(username)
        if (exists) {
            ctx.status = 409
            return;
        }
        const user = new User({ username })
        await user.setPassword(password)
        await user.save();
        ctx.body = user.serialize();
        const token = user.generateToken();

        ctx.cookies.set("access_token", token, {
            maxAge: 1000 * 60 * 60 * 24 * 7, httpOnly: true
        })
    } catch (e) {
        ctx.throw(500, e)
    }//


}

export const login = async ctx => {
    const { username, password } = ctx.request.body;

    // username, password κ°€ μ—†μœΌλ©΄ μ—λŸ¬ 처리
    if (!username || !password) {
        ctx.status = 401; // Unauthorized
        return;
    }

    try {
        const user = await User.findByUsername(username);
        // 계정이 μ‘΄μž¬ν•˜μ§€ μ•ŠμœΌλ©΄ μ—λŸ¬ 처리
        if (!user) {
            ctx.status = 401;
            return;
        }
        const valid = await user.checkPassword(password);
        // 잘λͺ»λœ λΉ„λ°€λ²ˆν˜Έ
        if (!valid) {
            ctx.status = 401;
            return;
        }
        ctx.body = user.serialize();
        const token = user.generateToken();
        ctx.cookies.set('access_token', token, {
            maxAge: 1000 * 60 * 60 * 24 * 7, // 7일
            httpOnly: true,
        })
    } catch (e) {
        ctx.throw(500, e);
    }
}

export const check = async ctx => {
    const { user } = ctx.state;


    if (!user) {
        ctx.status = 401; // Unauthorized
        return;
    }

    ctx.body = user;

}

export const logout = async ctx => {
    ctx.cookies.set("access_token");
    ctx.status = 204;
}

//auth/index.js

import Router from 'koa-router';
import * as authCtrl from './auth.ctrl';

const auth = new Router();

auth.post('/register', authCtrl.register);
auth.post('/login', authCtrl.login);
auth.get('/check', authCtrl.check);
auth.post('/logout', authCtrl.logout);

export default auth;

//jwtMiddleware.js
require("dotenv").config()
import jwt from "jsonwebtoken";
import User from "../models/user"

const jwtMiddleware = async (ctx, next) => {
    const token = ctx.cookies.get("access_token");

    if (!token) return next();
    try {
        const decoded = jwt.verify(token, process.env.JWT_SECRET);
        ctx.state.user = {
            _id: decoded._id,
            username: decoded.username
        }
        //ν† ν°μ˜ 남은 유효 기간이 3.5일 미만이면 μž¬λ°œκΈ‰
        const now = Math.floor(Date.now() / 1000);
        if (decoded.exp - now < 60 * 60 * 24 * 3.5) {
            const user = await User.findById(decoded_id);
            const token = user.generateToken();
            ctx.cookies.set("access_token", token, {
                maxAge: 1000 * 60 * 60 * 24 * 7, httpOnly: true
            })
        }
        return next();
    } catch (e) {
        return next()
    }
}

export default jwtMiddleware

0개의 λŒ“κΈ€