node 2022/02/07

무간·2022년 2월 7일

토큰 발행 모듈 설치import { useStore } from 'vuex';
백엔드 CMD npm i jsonwebtoken --save

파일명 routes/member.js

var express = require('express');
var router  = express.Router();

// config/mongodb.js 에서 DB내용 불러와서 연결
const db     = require('mongodb').MongoClient;
const dburl  = require('../config/mongodb').URL;
const dbname = require('../config/mongodb').DB;

// 문자를 HASH하기(암호보안)
const crypto = require('crypto');


// 토큰 발행을 위한 필요 정보 가져오기
// CMD> npm i jsonwebtoken
const jwt        = require('jsonwebtoken');
const { read } = require('fs');
const jwtKey     = require('../config/auth').securityKey;
const jwtOptions = require('../config/auth').options;
const checkToken = require('../config/auth').checkToken;


// 회원정보수정 put
// localhost:3000/member/update
// 토큰 이메일(PK) 이름(변경할 내용)
// checkToken으로 들어가서 점증 후 req로 들어감
router.put('/update',checkToken, async function(req, res, next) {
    try{
        console.log('이메일',req.body.uid);
        console.log('기존이름',req.body.uname);
        console.log('변경할 이름',req.body.name);

        // BD 연동
        const dbconn     = await db.connect(dburl);
        const collection = dbconn.db(dbname).collection('member1');

        // 정보변경
        const result = await collection.updateOne(         
            { _id : req.body.uid },
            { $set : { name : req.body.name } }
        );
        console.log(result);
        if(result.matchedCount === 1){
            return res.send({ status:200 });
        }
        
        // 결과값 리턴        
        
        return res.send({status : 0});
    }
    catch(e){
        console.error(e);
        res.send({status : -1, message:e});        
    }  
});


// 회원암호변경 put
// localhost:3000/member/updatepw
// 토큰 이메일, 현재암호 , 변경할 암호
router.put('/updatepw',checkToken, async function(req, res, next) {
    try{
        // 토큰에서 꺼낸 정보
        const email = req.body.uid;       // 토큰에서 꺼낸 정보
        const pw    = req.body.password;  // 현재 암호
        const pw1   = req.body.password1; // 변경할 암호

        // 2. 암호는 바로 비교 불가 회원가입과 동일한 hash후에 비교
        const hashPassword  = crypto.createHmac('sha256', email).update(pw).digest('hex');
        
    //==================================================================================

        const dbconn = await db.connect(dburl);
        const collection = dbconn.db(dbname).collection('member1');

        const result = await collection.findOne({
            _id : email, 
            pw  : hashPassword    
        });
    
        if(result !== null) { //로그인 가능
            //바꿀 암호를 hash
            const hashPassword1 = crypto.createHmac('sha256', email).update(pw1).digest('hex');
            
            const result1 = await collection.updateOne(
            { _id  : email },
            { $set : { pw : hashPassword1 } }
        );
            if(result1.modifiedCount === 1) {
                return res.send({status : 200});
              }
            }
        
            // 로그인 실패시
            return res.send({status : 0});
          }

    //==================================================================================

    //     const hashPassword1 = crypto.createHmac('sha256', email).update(pw1).digest('hex');       

    //     // BD 연동
    //     const dbconn     = await db.connect(dburl);
    //     const collection = dbconn.db(dbname).collection('member1');

    //     // 정보변경
    //     const result = await collection.updateOne(         
    //         {  _id : email, pw  : hashPassword },
    //         { $set : { pw : hashPassword1 } }
    //     );        
    //     console.log(result); 

    //     // 결과값 리턴        
    //     if(result.matchedCount === 1 ){
    //         return res.send({status : 200});
    //     }                        
    //     return res.send({status : 0});
    // }
    catch(e){
        console.error(e);
        res.send({status : -1, message:e});        
    }  
});




// 회원탈퇴 delete
// localhost:3000/member/delete
// 토큰 이메일, 현재 암호
router.delete('/delete', checkToken, async function(req, res, next) {
    try{
        // 토큰에서 꺼낸 정보
        const email = req.body.uid;
        const pw    = req.body.password;

        // 2. 암호는 바로 비교 불가 회원가입과 동일한 hash후에 비교
        const hashPassword = crypto.createHmac('sha256', email).update(pw).digest('hex');

        // 3. 회원정보가 일치하면 토큰을 발행        
        const dbconn     = await db.connect(dburl);
        const collection = dbconn.db(dbname).collection('member1');
        // 이메일과 hash한 암호가 둘다(AND) 일치
        const result = await collection.findOne({
            _id : email, 
            pw  : hashPassword
        });
        console.log(result);

        if(result !== null) { //로그인 가능            
            const result1 = await collection.deleteOne(
            { _id  : email }            
        );
            if(result1.deletedCount === 1) {
                return res.send({status : 200});
              }
            }

            // 로그인 실패시
            return res.send({status : 0});
          }

    catch(e){
        console.error(e);
        res.send({status : -1, message:e});        
    }  
});




// 로그인 post
// localhost:3000/member/select
// 이메일, 암호 => 현시점에 생성된 토큰을 전송
router.post('/select', async function(req, res, next) {
    try{
        // 1. 전송값 받기(이메일,암호)
        const email = req.body.email;
        const pw    = req.body.password;

        // 2. 암호는 바로 비교 불가 회원가입과 동일한 hash후에 비교
        const hashPassword = crypto.createHmac('sha256',email).update(pw).digest('hex');

        // 3. 회원정보가 일치하면 토큰을 발행        
        const dbconn     = await db.connect(dburl);
        const collection = dbconn.db(dbname).collection('member1');
        // 이메일과 hash한 암호가 둘다(AND) 일치
        const result = await collection.findOne({
            _id : email, 
            pw  : hashPassword
        });
        console.log(result);

        if(result !== null){// 로그인 가능
            const token = jwt.sign (
                { uid : email, uname : result.name }, // 토큰에 포함할 내용들..
                jwtKey,          // 토큰생성시 키값
                jwtOptions,      // 토큰 생성 옵션
            );
            return res.send({status : 200, token:token });
        }        
        return res.send({status : 0});
    }
    catch(e){
        console.error(e);
        res.send({status : -1, message:e});        
    }  
});


// 회원가입 post
// localhost:3000/member/insert
// 이메일(pk), 암호, 이름 받기
// 등록일 자동 생성
router.post('/insert', async function(req, res, next) {
    try{
        // 사용자1 aaa => ajsdklfqwermklnaocvoiajwlasd => 16진수
        // 사용자2 aaa => kjasfdiownfaiadfioqwemnkavsa => 16진수

        const hashPassword = crypto.createHmac('sha256',req.body.email).update(req.body.password).digest('hex');

        const obj={
            _id     : req.body.email,
            pw      : hashPassword,
            name    : req.body.name,
            regdate : new Date()
        }
        const dbconn     = await db.connect(dburl);
        const collection = dbconn.db(dbname).collection('member1');
        const result     = await collection.insertOne(obj);
        // console.log('========================');
        // console.log(result);
        // console.log('========================');
        // console.log(req.body);
        // console.log('------------------------');

        // 결과 확인
        if( result.insertedId === req.body.email ) {
            return res.send({status:200});
        }  
        return res.send({status : 0});
    }
    catch(e){
        console.error(e);
        res.send({status : -1, message:e});        
    }  
});

// 이메일 중복확인 get
// 이메일 => 결과
// localhost:3000/member/emailcheck?email=aaa@gmail.com
router.get('/emailcheck', async function(req, res, next) {
    try{        
        // db연결, db선택, 컬렉션선택
        const dbconn     = await db.connect(dburl);
        const collection = dbconn.db(dbname).collection('member1');
        const result     = await collection.countDocuments({
            _id : req.query.email
        });
        
        // 결과 확인 : 일치하는 개수 리턴 0 또는 1 
        return res.send({status : 200, result : result});
    }
    catch(e){
        console.error(e);
        res.send({status : -1, message:e});        
    }  
});




module.exports = router;

=============================================

파일명 config/auth/js

// 파일명 : config/auth.js
// 토큰 생성

const jwt = require('jsonwebtoken');

const self = module.exports = {
    securityKey : 'asdjfkl;awejkl;234jkla;sdf',
    options     : {
        algorithm : 'HS256', // 알고리즘 
        expiresIn : '10h',   // 만료시간
        issuer    : 'DS',    // 발행자
    },

    // 토큰의 유효성 검사를 위한 함수, authfilter
    // 검사중 res가 나오면 오류
    // 정상적인 검증이면 req로 받아서 next로 memver.js에서 실행함
    checkToken : async(req, res, next) => {
        try{
            const token = req.headers.token;
            if(!token){
                return res.send({ status:-1, result:'토큰값이 없습니다.' });
            }

            // 토큰에서 필요한 값을 추출
            // 토큰 생성시 사용했든 securitykey가 필요
            const user = jwt.verify(token, self.securityKey); // 토큰값에 문제가 있을 경우 catch로 갑

            if(typeof user.uid === 'undefined'){
                return res.send({ status:-1, result:'정보 추출 불가' });
            }
            if(typeof user.uname === 'undefined'){
                return res.send({ status:-1, result:'정보 추출 불가' });
            }

            // 추출이 가능하다면 req.body에 임의으 키값으로 추가함
            req.body.uid   = user.uid;
            req.body.uname = user.uname;

            // member.js의 router가 동작됨
            next();
        }
        catch(e){
            if(e.massage === 'invalid signature'){
                return res.send({ status:-1, result:'인증 실패' });
            }
            if(e.massage === 'jwt expired'){
                return res.send({ status:-1, result:'시간 만료' });
            }
            if(e.massage === 'invalid token'){
                return res.send({ status:-1, result:'유효하지 않은 토큰' });
            }
            return res.send({status : -1 , result:'토큰 오류'});
        }

    }
}

=============================================

파일명 vue.config.js

module.exports = {
    devServer : {
        // 벡엔드의 주소를 짧게 사용하기 위해서
        // http://localhost:3000/board/select => board/select
        proxy : {
            '/board' :{
                target       : 'http://localhost:3000',
                changeOrigin : true,
                logLevel     : 'debug'
            },
            
            '/member' :{
                target       : 'http://localhost:3000',
                changeOrigin : true,
                logLevel     : 'debug'
            }
        },
        port : 8080
    },
    
}

=============================================

파일명 src/component/Join.vue

<template>
    <div class="style1">
        <h3>src/component/Join.vue</h3>
            
    {{state}}
    <hr/>
    

    <el-form :inline="true" >
        <el-form-item label-width="120px" label="이메일" >
            <el-input ref="userid" v-model="state.uid" @keyup="handleEmailCheck"></el-input>
        </el-form-item>
    </el-form>

    <div>{{state.useremailcheck}}</div><br />

    <el-form :inline="true" >
        <el-form-item label-width="120px" label="암호" >
            <el-input ref="userpw" type="password" v-model="state.upw"></el-input>
        </el-form-item>
    </el-form>

    <el-form :inline="true" >
        <el-form-item label-width="120px" label="암호확인" >
            <el-input ref="userpw1" type="password" v-model="state.upw1"></el-input>
        </el-form-item>
    </el-form>

    <el-form :inline="true" >
        <el-form-item label-width="120px" label="이름" >
            <el-input ref="username" v-model="state.uname" @keyup="handleEmailCheck"></el-input>
        </el-form-item>
    </el-form>

    <el-form :inline="true" >
        <el-form-item label-width="120px" label=" " >
            <el-button style="width:100px" @click="handleJoin"  type="primary">회원가입</el-button>
            <el-button style="width:100px" type="primary">취소</el-button>
        </el-form-item>
    </el-form>
           

    </div>
</template>

<script>
import { reactive, ref } from 'vue';
import { useRouter } from 'vue-router';
import axios from "axios";

export default {
    setup () {        
        const router = useRouter();
        // High레벨 변수 생성 : 오브젝트만 변화 감지
        const state = reactive({
            uid     : '',
            upw     : '',
            upw1    : '',
            uname   : '',            
            useremailcheck : '중복확인' 
        });
         // Low레벨 변수 생성 : 오브젝트가 아님
        const userid     = ref(null); // 위에서 연결하면 bbb값은 의미가 없어짐
        const userpw     = ref(null); // ref 안쪽에 모든 데이터가
        const userpw1    = ref(null);
        const username   = ref(null);
        

        const validEmail = (email) => {
            // 정규 표현식
            var re = /[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]*$/i;
            return re.test(email);
        }

        const handleEmailCheck = async() => {
            // 입력한 내용이 이메일형식이면 벡엔드로 전송후 중복유무 확인
            if(validEmail(state.uid)){
                console.log(state.uid);
                const url = `/member/emailcheck?email=${state.uid}`;
                const headers = {"Content-Type":"application/json"};
                const response = await axios.get(url, {headers});
                console.log(response.data);
                if(response.data.status===200){
                    if(response.data.result === 1){
                        state.useremailcheck='사용불가';
                    }
                    else{
                        state.useremailcheck='사용가능';
                    }
                }
            }
            else{
                state.useremailcheck='중복확인';
            }
        }
       

        // function handleJoin( ){ }
        const handleJoin = async() =>{

            if(state.uid === ''){
                alert('아이디를 입력 하세요');
                userid.value.focus();
                return false; // 이 위치에서 메소드 종료
                
            }

            if(state.upw === ''){
                alert('비밀번호를 입력하세요');
                userpw.value.focus();
                return false;
            }

            if(state.upw1 !== state.upw){
                alert('비밀번호가 일치하지 않습니다');
                userpw1.value.focus();
                return false;
            }
            if(state.uname === ''){
                alert('아이디를 입력 하세요');
                username.value.focus();
                return false; // 이 위치에서 메소드 종료
                
            }           
            
            if(state.useremailcheck !== '사용가능') {
                alert('이메일중복체크하세요.');
                userid.value.focus();
                return false;
            }

            // 유효성 검증완료되는 시점에 백엔드 연동
            const url = `/member/insert`;
            const headers = {"Content-Type":"application/json"};
            const body = {
                email     : state.uid,
                password  : state.upw,
                name      : state.uname
             }
            const response = await axios.post(url, body, {headers});
            console.log(response.data);
            if(response.data.status === 200 ){
                alert('회원가입 완료');
                router.push({name:'Home'});
                
            }

            
        };
        
        

        return {userid, userpw, userpw1, username, state, handleJoin, handleEmailCheck}
    }
}
</script>

<!-- scss,less => css -->
<!-- npm install -D sass-loader@^10 sass -->
<style lang="scss" scoped>
    .style1{
        border : 1px solid #cccccc;
        padding : 20px;
    }

</style>

=============================================

파일명 src/component/Login.vue

<template>
    <div>
        <h3>src/component/Login.vue</h3>
        {{state}}
        <hr />
        <input type="text" v-model="state.userid"/>
        <input type="password" v-model="state.userpw"/>
        <input type="button" value="로그인" @click="handleLogin"/>

    </div>
</template>

<script>
import { reactive } from 'vue';
import axios from 'axios';
import { useRouter } from 'vue-router';
import { useStore } from 'vuex';


export default {
    // ver 3.0
    setup () { // this를 사용할 수 없음
        const router = useRouter();
        const store  = useStore();

        const state  = reactive({
            userid : 'aaa@gmail.com',
            userpw : 'aaa',

        });
        const handleLogin = async() => {
            const url = `/member/select`;
            const headers = {"Content-Type":"application/json"};
            const body = {
                email    : state.userid,
                password : state.userpw
            };
            const response = await axios.post(url, body, {headers});
            console.log(response.data.status);

            if(response.data.status === 200){
                console.log(response.data.token)
                sessionStorage.setItem("TOKEN", response.data.token); 
                alert('로그인 되었습니다.');

                // 주소창만 바뀜
                router.push({name:'Home'});
                // app.vue에 메뉴의 선택항목을 변경하도록 알려줌
                store.commit("setMenu","/")
            }
            else{
                alert('아이디와 암호를 확인하세요');
            }

            
        };
        return {state, handleLogin}
    
    },   

}
</script>

<style lang="scss" scoped>

</style>
profile
당신을 한 줄로 소개해보세요

0개의 댓글