import mongoose, {Schema} from 'mongoose';
const UserSchema = new Schema({
username: String,
hashedPassword: String,
})
const User = mongoose.model('User', UserSchema);
export default User;
const user = new User({username: 'velopert'})
user.setPassword('mypass123')
스태틱 메서드( 모델에서 바로 사용할 수 있는 함수) 로 나뉨
const user = User.findByUsername('velopert')
import mongoose, {Schema} from 'mongoose';
import bcrypt from 'bcrypt';
const UserSchema = new Schema({
username: String,
hashedPassword: String,
})
//인스턴스 메서드 - this는 문서 인스턴스(UserSchema)를 가리킴
UserSchema.methods.setPassword=async
function(password){
const hash = await bcrypt.hash(password, 10);
this.hashedPassword = hash;
}
UserSchema.methods.checkPassword= async function(password){
const result = await bcrypt.compare(password, this.hashedPassword);
return result;
}
// 스태틱 메서드 -this는 모델(User)을 가리킴
UserSchema.statics.findByUsername=
function(username) {
return this.findOne({ username});
}
const User = mongoose.model('User', UserSchema);
export default User;
import Joi from 'joi';
import User from '../../models/user';
/**
POST /api/auth/register
{
username: 'velopert'
password: 'mypass123
}
*/
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 {
//username 이 이미 존재하는지 확인
const exists = await User.findByUsername(username);
if (exists) {
ctx.status = 409;
return;
}
const user = new User({
username,
});
await user.setPassword(password);
await user.save();
//응답할 데이터에서 hashedPassword 필드 제거
ctx.body = user.serialize();
} 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 = 41;
return;
}
const valid = await user.checkPassword(password);
//잘못된 비밀번호
if (!valid) {
ctx.status = 401;
return;
}
ctx.body = user.serialize();
} catch (e) {
ctx.throw(500, e);
}
};
UserSchema.methods.serialize = function () {
const data = this.toJSON();
delete data.hashedPassword;
return data;
};
PORT=4001
MONGO_URI=mongodb://localhost:27017/blog
JWT_SECRET=안녕하세요
UserSchema.methods.generateToken = function () {
const token = jwt.sign(
{
_id: this.id,
username: this.username,
},
process.env_JWT_SECRET,
{ expiresIn: '7d' },
);
return token;
};
1)localStorage 혹은 sessionStorage
2) 브라우저 쿠키에 담기
즉, 2번이 낫다.
UserSchema.methods.generateToken = function () {
const token = jwt.sign(
{
_id: this.id,
username: this.username,
},
process.env.JWT_SECRET,
{ expiresIn: '7d' },
);
return token;
};
import Joi from 'joi';
import User from '../../models/user';
/**
POST /api/auth/register
{
username: 'velopert'
password: 'mypass123
}
*/
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 {
//username 이 이미 존재하는지 확인
const exists = await User.findByUsername(username);
if (exists) {
ctx.status = 409;
return;
}
const user = new User({
username,
});
await user.setPassword(password);
await user.save();
//응답할 데이터에서 hashedPassword 필드 제거
ctx.body = user.serialize();
//jwt 토큰
const token = user.generateToken();
ctx.cookies.set('access_token', token, {
maxAge: 100 * 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 = 41;
return;
}
const valid = await user.checkPassword(password);
//잘못된 비밀번호
if (!valid) {
ctx.status = 401;
return;
}
ctx.body = user.serialize();
//jwt 토큰
const token = user.generateToken();
ctx.cookies.set('access_token', token, {
maxAge: 100 * 60 * 60 * 24 * 7,
httpOnly: true,
});
} catch (e) {
ctx.throw(500, e);
}
};
import jwt from 'jsonwebtoken';
const jwtMiddleware = (ctx, next) => {
const token = ctx.cookies.get('access_token');
if (!token) return next();
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
console.log(decoded);
return next();
} catch (e) {
//토큰 검증 실패
return next();
}
};
export default jwtMiddleware;
require('dotenv').config();
import Koa from 'koa';
import Router from 'koa-router';
import bodyParser from 'koa-bodyparser';
import mongoose from 'mongoose';
import api from './api';
import jwtMiddleware from './lib/jwtMiddleware';
//비구조화 할당을 통해 process.env 내부 값에 대한 레퍼런스 만들기
const { PORT, MONGO_URI } = process.env;
mongoose
.connect(MONGO_URI, { useNewUrlParser: true, useFindAndModify: false })
.then(() => {
console.log('connected to MongoDB');
})
.catch((e) => {
console.error(e);
});
const app = new Koa();
const router = new Router();
//라우터 설정
router.use('/api', api.routes());
// 라우터 적용 전에 bodyParser 적용
app.use(bodyParser());
app.use(jwtMiddleware);
// app 인스턴스에 라우터 적용
app.use(router.routes()).use(router.allowedMethods());
//[PST가 지정되어 있지 않다면 4000을 사용
const port = PORT || 4000;
app.listen(port, () => {
console.log('Listening to port %d', port);
});
export const check = async (ctx) => {
//로그인 상태 구현
const { user } = ctx.state;
if (!user) {
//로그인 중 아님
ctx.status = 401;
return;
}
ctx.body = user;
};
/*
export const logout = async (ctx) => {
//로그아웃
};
*/
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);
// 이후 미들웨어에서 사용할 수 있도록 state에 넣어주기
ctx.state.user = {
_id: decoded._id,
username: decoded.username,
};
//토큰의 남은 유효기간이 3.5일 미만이면 재발급
const now = Math.flooer(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,
});
}
console.log(decoded);
return next();
} catch (e) {
//토큰 검증 실패
return next();
}
};
export default jwtMiddleware;