β‘οΈ νλ‘μ νΈ μ 리
- JWT(JSON Web Token)μ μ΄μ©νμ¬ μλ²μ νμ μΈμ¦ μμ€ν
μ ꡬνν΄λ³΄λ νλ‘μ νΈλ₯Ό ν΄λ³΄μμ΅λλ€.
- μΈμ κΈ°λ° μΈμ¦ μμ€ν
κ³Ό ν ν° κΈ°λ° μΈμ¦ μμ€ν
μ€ ν ν° κΈ°λ° μΈμ¦ μμ€ν
μ μ¬μ©νλλ° μ΄ μμ€ν
μ μ¬μ©μ λ‘κ·ΈμΈ μ 보λ₯Ό κΈ°μ΅νλλ° νμν 리μμ€κ° μ λ€λ μ₯μ μ κ°μ§κ³ μμ΅λλ€.
- κ·Έ μΈμ ꡬνμ κ°νΈμ±κ³Ό μ¬μ©μλ€μ μΈμ¦ μνλ₯Ό κ΄λ¦¬νκΈ° νΈνλ€λ μ₯μ λν κ°μ§κ³ μμ΅λλ€.
- JWT ν ν°μ λ§λ€κΈ° μνμ¬ jsonwebtoken λͺ¨λ μ€μΉκ° νμν©λλ€.
- μ¬μ©μκ° ν ν°μ μ¬μ©ν λ μ£Όλ‘ λΈλΌμ°μ μ localstorage λλ sessionstorageμ 보κ΄νκ±°λ λΈλΌμ°μ μ μΏ ν€μ λ΄μμ μ¬μ©ν©λλ€.
- μ μμ κ²½μ° XXS(Cross Site Scripting) 곡격μ μ·¨μ½νλ©° νμμ κ²½μ°, CSRF(Cross Site Request Forgery) 곡격μ μ·¨μ½ν©λλ€.
- μ΄μμΌλ‘ λ°±μλ ννΈλ λ§λ¬΄λ¦¬λμμΌλ©° 리μ‘νΈλ₯Ό μ΄μ©νμ¬ λΈλ‘κ·Έ μΉ μ ν리μΌμ΄μ
μ λ§λ€ μμ μ
λλ€.
π μ½λ
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;
if (!username || !password) {
ctx.status = 401;
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,
httpOnly: true,
})
} catch (e) {
ctx.throw(500, e);
}
}
export const check = async ctx => {
const { user } = ctx.state;
if (!user) {
ctx.status = 401;
return;
}
ctx.body = user;
}
export const logout = async ctx => {
ctx.cookies.set("access_token");
ctx.status = 204;
}
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;
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
}
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