토큰 발행 모듈 설치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>