20220106


--バックエンド
CMD> nodemon --inspect ./bin/www
--フロントエンド
CMD> npm run serve
//照会:thisを待つ.axios.get(url, {headers:headers});
//追加:thisを待ちます.axios.post(url, body, {headers:headers});
//修正:これを待つ.axios.put(url, body, {headers:headers});
//削除:thisを待ちます.axios.delete(url, {headers:headers, data:{}});
//ファイル名:config/auth.js
//トークンの作成、抽出、検証が必要
const jwt = require('jsonwebtoken');
const self = module.exports = {
securityKey : 'fjifej$%e_ijfj39890uu94343',
options : {
アルゴリズム:“HS 256”,//トークン生成hashアルゴリズム
expiressin:9 h,//トークン有効期限ex)9時間
発行者:「corp 01」//トークン発行者
},
// 토큰이 전달되면 토큰의 유효성을 검증함.
checkToken : async(req, res, next) => {
    try {
        const token = req.headers.token;
        // 1. 토큰이 있느냐?
        if(!token){
            return res.send({status:-1, result:'토큰이 없음.'});
        }
        // 2. 토큰 decode 추출(토큰과 암호키)
        const user = jwt.verify(token, self.securityKey);

        if(typeof user.uid === 'undefined'){
            return res.send({status:-1, result:'유효하지 않는 토큰'});
        }

        console.log('토큰에서 추출한 아이디 => ', user.uid);

        // 수동으로 body에 포함(아이디, 이메일, 나이)
        req.body.userid = user.uid;

        // 위쪽에서 토큰에 대한 유효성을 모두 pass할경우 다음으로 넘김
        // member.js가 호출 (이메일, 나이)
        next();
    }
    catch(err){
        console.error(err);
        if(err.message === 'invalid signature'){
            return res.send({status:-1, result : "인증실패"});
        }
        else if(err.message === 'jwt expired') {
            return res.send({status:-1, result : "시간만료"});
        }
        else if(err.message === 'invalid token'){
            return res.send({status:-1, result : "유효하지 않는 토큰"});
        }
        return res.send({status:-1, result : err});
    }
}
}
//ファイル名:member.js
var express = require('express');
var router = express.Router();
//モンゴルDBバインド
//CMD> npm i mongodb --save
const db = require('mongodb').MongoClient;
const DBURL = require('../config/db').mongodbURL;
const DBNAME = require('../config/db').mongodbDB;
//ログイン時にトークンを発行する
//CMD> npm i jsonwebtoken --save
const jwt = require('jsonwebtoken');
const jwtKey = require('../config/auth').securityKey;
const jwtOptions = require('../config/auth').options;
const checkToken = require('../config/auth').checkToken;
//会員入力、ログイン時のパスワードhash
const crypto = require('crypto');
//会員情報の伝達http://localhost:3000/member/selectone
router.get('/selectone', checkToken, async function(req, res, next) {
try {
//身分:req.body.userid(auth.jsに手動で転送!)
const dbConn = await db.connect(DBURL);
const coll = dbConn.db(DBNAME).collection("member");
    const result   = await coll.findOne(
        { _id : req.body.userid },    // 조건
        { projection : { userpw:0, _id:0 } } // 필요없는 항목제거
    );
    return res.send({status:200, result:result});
}
catch(err) {
    console.error(err);
    return res.send({status:-1, result : err});
}
});
//会員情報修正:http://localhost:3000/member/mypage?menu=1
//パスワード変更:http://localhost:3000/member/mypage?menu=2
//会員脱退:http://localhost:3000/member/mypage?menu=3
router.put('/mypage', checkToken, async function(req, res, next) {
try {
//console.log( req.query.menu ); // 結果について
//console.log( typeof req.query.menu );//タイプ情報
    const menu = Number(req.query.menu);

    const dbConn   = await db.connect(DBURL);
    const coll     = dbConn.db(DBNAME).collection("member");

    if (menu === 1) {
        const result = await coll.updateOne(
            { _id : req.body.userid }, //조건
            { $set : { 
                    userage: req.body.userage, 
                    useremail:req.body.useremail
                }  
            },// 변경내용
        );

        console.log(result); // 실패 or 성공
        if(result.modifiedCount === 1){
            return res.send({stauts:200});
        }
        return res.send({status: 0});
    }
    else if(menu === 2) {
        // req.body.userid  아이디(변경X)
        // req.body.userpw  현재암호
        // req.body.userpw1 바꿀암호

        const hash = crypto.createHmac('sha256', req.body.userid)
            .update(req.body.userpw).digest('hex');

        const hash1 = crypto.createHmac('sha256', req.body.userid)
            .update(req.body.userpw1).digest('hex');

        const result = await coll.updateOne(
            //조건 아이디와 현재암호가 일치하는 조건
            { _id : req.body.userid, userpw:hash },
            
            //변경할 내용 바꿀암호
            { $set : {userpw: hash1 }}  
        );

        console.log(result); // 실패 or 성공
        if(result.modifiedCount === 1){
            return res.send({stauts:200});
        }
        return res.send({status: 0});
    }
    else if(menu === 3) {
        // req.body.userid  아이디
        // req.body.userpw  암호
        const hash = crypto.createHmac('sha256', req.body.userid)
            .update(req.body.userpw).digest('hex');

        const result = await coll.deleteOne(
            { _id:req.body.userid, userpw:hash } //삭제 조건 : 아이디, 암호일치할경우
        );

        console.log(result);
        // 삭제보다는 필요시에 중요정보를 updateOne.
        if(result.deletedCount === 1) {
            return res.send({status:200});
        }
        return res.send({status:0}); 
    }

    return res.send({status:-1, result:"메뉴정보 없음"});
}
catch(err) {
    console.error(err);
    return res.send({status:-1, result : err});
}
});
//ログイン:http://localhost:3000/member/select
//パスワード情報があるので、post
router.post('/select', async function(req, res, next) {
try {
//会員登録時に使用しなければならない暗号化方式hash
登録比較は//DBから行えます.
const hash = crypto.createHmac('sha256', req.body.uid)
.update(req.body.upw).digest('hex');
    const obj = {
        userid : req.body.uid,
        userpw : hash
    };

    console.log(obj);

    const dbConn   = await db.connect(DBURL);
    const coll     = dbConn.db(DBNAME).collection("member");

    // const query = { $and : [{_id:obj.userid}, {userpw:obj.userpw}] };
    const query    = { _id:obj.userid, userpw:obj.userpw };
    const result   = await coll.findOne(query);

    console.log(result);  // 실패 or 성공
    if(result !== null){ // DB에 일치하는 경우 
        const token = {
            token : jwt.sign( 
                { uid : result._id },     // 토큰에 포함할 내용들...
                jwtKey,                 // 토큰생성 키 
                jwtOptions              // 옵션
            ),
            refreshToken : null,        // null
        }
        return res.send({status:200, result : token});    
    }

    // 일치하지 않을 경우
    return res.send({status:0});
}
catch(err) {
    console.error(err);
    return res.send({status:-1, result : err});
} 
});
//会員入力:http://localhost:3000/member/insert
router.post('/insert', async function(req, res, next) {
try{
console.log(req.body);
//hash(salt)はabc->io 3 rj 987 io 23873987437839 ufj 3983 r
const hash = crypto.createHmac('sha256', req.body.uid)
.update(req.body.upw).digest('hex');
const obj = {
_id : req.body.uid,
userpw : hash,
userage : Number(req.body.uage),
userbirth : req.body.ubirth,
useremail : req.body.uemail,
usercheck : req.body.ucheck,
usergender : Number(req.body.ugender)
};
console.log(obj);
    const dbConn   = await db.connect(DBURL);
    const coll     = dbConn.db(DBNAME).collection("member");

    const result = await coll.insertOne(obj); //{ }
    console.log(result); // 성공 or 실패
    if( result.insertedId === obj._id ) {
        return res.send({status:200});
    }
    return res.send({status:0});
}
catch(err) {
    console.error(err);
    return res.send({status:-1, result : err});
} 
});
//重複認証:http://localhost:3000/member/idcheck?uid=aa
router.get('/idcheck', async function(req, res, next) {
try {
const userid = req.query.uid;
    const dbConn   = await db.connect(DBURL);
    const coll     = dbConn.db(DBNAME).collection("member");

    const query    = { _id : userid };
    const result   = await coll.countDocuments(query);

    console.log(result); //{  }
    return res.send({status:200, result:result});
}
catch(err) {
    console.error(err);
    return res.send({status:-1, result : err});
} 
});
module.exports = router;
============================================
//ファイル名:Mypage.vue
<div>
    <h4>마이페이지</h4>
    <el-card shadow="always">
        <el-tabs tab-position="left" style="height: 200px">
            <el-tab-pane label="정보수정">
                <menu-1></menu-1>
            </el-tab-pane>
            <el-tab-pane label="비밀번호변경">
                <menu-2></menu-2>
            </el-tab-pane>
            <el-tab-pane label="회원탈퇴">
                <menu-3></menu-3>
            </el-tab-pane>
        </el-tabs>
    </el-card>
</div>
//ファイル名:mypage/Menu 1.vue
<div>
    <el-form :inline="true" class="demo-form-inline" style="margin-bottom:-20px">
        <el-form-item label="나이" label-width="120px">
            <el-input v-model="member.userage" ref="userage" size="mini" placeholder="이름"></el-input>
        </el-form-item>            
    </el-form>

    <el-form :inline="true" class="demo-form-inline" style="margin-bottom:-20px">
        <el-form-item label="이메일" label-width="120px">
            <el-input v-model="member.useremail1" ref="useremail" size="mini" placeholder="이메일"></el-input>
        </el-form-item>
        <el-form-item >@</el-form-item>
        <el-form-item >
            <el-select v-model="member.useremail2" ref="useremail1" size="mini" placeholder="선택">
                <el-option v-for="tmp in emailOption" :key="tmp" 
                    :label="tmp" :value="tmp"></el-option>
            </el-select>
        </el-form-item>        
    </el-form>
    
    <el-form :inline="true" class="demo-form-inline" style="margin-bottom:-20px">
        <el-form-item label=" " label-width="120px">
            <el-button type="primary" size="mini" @click="handleUpdate">수정하기</el-button>
        </el-form-item>            
    </el-form>
    
</div>