IT/프로젝트

8. boilerplate - 로그인 구현 with jsonwebtoken

반응형

boilerplate 유튜브 강의 시리즈

Blog ReactJS NodeJS#13 LOGIN FUNCTION WITH JSONWEBTOKEN

 

이번 강의에서는 JWT(jsonwebtoken)을 사용하여 로그인을 구현한다.

JWT는 정보를 안전하게 전송하기 위해 정의된 공개된 표준 (RFC 7519) 이다. (자세한 설명은 여기)

 

1. jsonwebtoken 모듈 및 type definition 패키지 설치

npm i -S jsonwebtoken
npm i -D @types/jsonwebtoken

 

2. UserSchema에 로그인 시 사용할 comparePassword, generateToken 메소드 추가

 

jsonwebtoken 모듈을 import 하고 UserSchema에 comparePassword, generateToken 메소드를 추가한다.

추가한 메소드의 타입은 IUser 인터페이스에도 정의해준다.

import { Schema, Model, model, Document, HookNextFunction } from 'mongoose';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
const saltRounds = 10;

export interface IUser extends Document{
    name: string;
    email: string;
    password: string;
    lastname: string;
    role: number;
    token: string;
    tokenExp: number;
    comparePassword: (plainPassword:string, cb:Function) => void;
    generateToken: (cb:Function) => void;
}

const UserSchema: Schema<IUser> = new Schema<IUser>({
    name: {
        type:String,
        maxlength:50
    },
    email: {
        type:String,
        trim:true,
        unique: 1
    },
    password: {
        type:String,
        minlength: 5
    },
    lastname: {
        type:String,
        maxlength: 50
    },
    role: {
        type:Number,
        default: 0
    },
    token: {
        type:String,
    },
    tokenExp: {
        type: Number
    }
});

UserSchema.pre<IUser>('save', function(next:HookNextFunction){
    if(this.isModified('password')){
        bcrypt.genSalt(saltRounds, (err:Error, salt:string)=>{
            if(err) return next(err);
    
            bcrypt.hash(this.password, salt, (err:Error, hash:string)=>{
                if(err) return next(err);
                this.password = hash;
                next();
            })
        })
    }else{
        next();
    }
});

UserSchema.methods.comparePassword = function(plainPassword:string, cb:Function){
    bcrypt.compare(plainPassword, this.password,(err:Error, isMatch:boolean)=>{
        if(err) return cb(err);
        cb(null, isMatch);
    });
}

UserSchema.methods.generateToken = function(cb:Function) {
    this.token = jwt.sign(this._id.toHexString(), 'secret');
    
    this.save((err:Error, user:IUser)=>{
        if(err) return cb(err);
        cb(null, user);
    })
}

export const User: Model<IUser> = model<IUser>('User', UserSchema);

 

3. index.ts에 로그인 api 추가

 

위에서 추가한 2가지 메소드를 로그인 로직에서 활용한다.

코드를 살펴보면 첫 번째로 email 검증을 하고 두 번째로 comparePassword 메소드로 패스워드 검증을 한다.

마지막으로 generateToken 메소드로 token을 생성하고 token을 담아 반환한다.

app.post('api/user/login', (req:express.Request, res: express.Response)=>{
    User.findOne({email: req.body.email}, (err:Error, user:IUser)=>{
        if(!user){
            return res.json({
                loginSuccess: false,
                message: "Auth failed, email not found"
            });
        }

        user.comparePassword(req.body.password, (err:Error, isMatch:boolean)=>{
            if(!isMatch){
                return res.json({ loginSuccess: false, message: "wrong password"});
            }
        })

        user.generateToken((err:Error, user:IUser)=>{
            if(err) return res.status(400).send(err);
            res.cookie("x_auth", user.token)
                .status(200)
                .json({
                    loginSuccess: true
                })
        })
    })
})

 

반응형