node 2022/02/18
90222 ワード
ファイル名/ルータ/メンバー.js
ファイル名src/componse/Join.vue
ファイル名src/componse/LOgin.vue
ファイル名src/App.vue
ファイル名src/stores/index.js
ファイル名config/auth.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 jwtKey = require('../config/auth').securityKey;
const jwtOptions = require('../config/auth').options;
const checkToken = require('../config/auth').checkToken;
// 토큰이 오면 정보를 전송함
// localhost:3000/member/validation
router.get('/validation', checkToken, async function(req, res, next) {
try {
return res.send({
status : 200,
uid : req.body.uid,
uname : req.body.uname,
urole : req.body.urole,
});
}
catch (e) {
console.error(e);
res.send({status:-1, message : e});
}
});
// 주소등록
// localhost:3000/member/insertaddr
// vue에서는 토큰 입력할 주소
router.post('/insertaddr', checkToken, async function(req, res, next) {
try {
// DB접속
const dbconn = await db.connect(dburl);
// 2. DB선택 및 컬렉션 선택(sequence값 1씩 증가시키는 DB)
const collection = dbconn.db(dbname).collection('sequence');
const result = await collection.findOneAndUpdate(
{ _id : 'SEQ_MEMBERADDR1_NO' }, // 가지고 오기 위한 조건
{ $inc : {seq : 1 } } // seq값을 1증가씨킴
);
const obj = {
_id : result.value.seq, //시퀀스값
address : req.body.address, //주소정보
memberid : req.body.uid, // 토큰에서 꺼내기
chk : 0, // 대표주소설정 (숫자크면 우선순위 부여)
regdate : new Date()
}
const collection1 = dbconn.db(dbname).collection('memberaddr1');
const result1 = await collection1.insertOne(obj);
console.log(result1);
if(result1.insertedId === result.value.seq){
return res.send({ status : 200 });
}
return res.send({ status : 0 });
}
catch(e) {
console.error(e);
res.send({status : -1, message:e});
}
});
// 주소목록
// localhost:3000/member/selectaddr
router.get('/selectaddr', checkToken, async function(req, res, next) {
try{
const email = req.body.uid;
const dbconn = await db.connect(dburl);
const collection = dbconn.db(dbname).collection('memberaddr1');
const result = await collection.find(
{ memberid:email },
{projection : {address:1}}
).sort({ _id: 1 }).toArray();
return res.send({status:200, result:result});
}
catch(e) {
console.error(e);
res.send({status : -1, message:e});
}
});
// 주소삭제
// localhost:3000/member/deleteaddr
router.delete('/deleteaddr', checkToken, async function(req, res, next) {
try{
const code = req.body.code;
const dbconn = await db.connect(dburl);
const collection = dbconn.db(dbname).collection('memberaddr1');
const result = await collection.deleteMany(
{ _id : {$in : code} }
)
console.log(result);
if(result.deletedCount === code.length){
return res.send({status : 200});
}
return res.send({status : 0});
}
catch(e) {
console.error(e);
res.send({status : -1, message:e});
}
});
// 주소수정
router.put('/updateaddr', checkToken, async function(req, res, next) {
});
// 대표주소설정
router.put('/updatechkaddr', checkToken, async function(req, res, next) {
});
// 회원정보수정 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');
console.log(email);
console.log(hashPassword);
//==================================================================================
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, urole: result.role }, // 세션 => 토큰에 포함할 내용들(아이디, 이름, 권한)
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,
role : req.body.role,
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/[email protected]
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;
===========================================ファイル名src/componse/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>
{{state.useremailcheck}}
</el-form>
<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="권한" >
<select v-model="state.userrole">
<option value="CUSTOMER">고객</option>
<option value="SELLER">판매자</option>
</select>
</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 : '',
userrole: 'CUSTOMER',
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,
role : state.userrole,
}
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/componse/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 : '[email protected]',
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('로그인 되었습니다.');
// stoer의 actions를 호출할 경우
store.dispatch("handleData", {});
const curl = sessionStorage.getItem("CURL");
if(curl === null){
// 주소창만 바뀜
router.push({name:'Home'});
// app.vue에 메뉴의 선택항목을 변경하도록 알려줌
// 메뉴 활성화
store.commit("setMenu","/");
}
else {
const query = JSON.parse(sessionStorage.getItem("CURL_QUERY"));
const params = JSON.parse(sessionStorage.getItem("CURL_PARAMS"));
router.push({name:curl, query:query, params:params});
}
}
else{
alert('아이디와 암호를 확인하세요');
}
};
return {state, handleLogin}
},
}
</script>
<style lang="scss" scoped>
</style>
===========================================ファイル名src/App.vue
<template>
<div>
<!-- ElMenu.vue -->
<!-- props로 default-active숫자값을 전달 -->
<!-- props로 router TRUE값을 전달 -->
<!-- 자식컴포넌트 emit 를 통해서 select이벤트 -->
<el-menu :default-active="state.activeIndex"
class="el-menu-demo"
mode="horizontal"
:router="true"
@select="handleSelect">
<el-menu-item index="/">Home</el-menu-item>
<el-menu-item v-show="logged === false" index="/login">Login</el-menu-item>
<el-menu-item v-show="logged === true" index="/logout">Logout</el-menu-item>
<el-menu-item v-show="logged === true && urole === 'SELLER'" index="/seller">Seller</el-menu-item>
<el-menu-item v-show="logged === true && urole === 'CUSTOMER'" index="/mypage">Mypage</el-menu-item>
<el-menu-item index="/board">Board</el-menu-item>
<el-menu-item index="/admin">Admin</el-menu-item>
<el-menu-item index="/join">Join</el-menu-item>
<el-menu-item index="/chart">Chart</el-menu-item>
<div style="padding:17px" v-if="logged === true">{{uid}}//{{uname}}님 로그인</div>
</el-menu>
{{menu}},{{logged}}
<hr />
<router-view></router-view>
</div>
</template>
<script>
import {useStore} from 'vuex';
import {computed, reactive, onMounted } from 'vue';
export default {
setup () {
const store = useStore();
// store의 menu값 실시간으로 가져오기
// 마지막으로 방문한 페이지를 session저장소에 보관후에 변환
const menu = computed(()=>{
return store.getters.getMenu
});
// store의 logged값 실시간으로 확인
const logged = computed(()=>{
return store.getters.getLogged
});
const uid = computed(()=>{
return store.getters.getUid
});
const uname = computed(()=>{
return store.getters.getUname
});
// SELLER,CUSTOMER
const urole = computed(()=>{
return store.getters.getUrole
});
// store에서 읽은 메뉴값으로 초기값으로 세팅
const state = reactive({
activeIndex : menu
})
onMounted( async () => { // F5 새로고침 했을때
// 저장소에 있는 TOKEN값을 읽어서 존재 유무 확인
// TOKEN을 추가하는 시점은 로그인이 완료 되었을때
// TOKEN의 값을 제거하는 시점은 로그아웃되었을때
if(sessionStorage.getItem("TOKEN")===null){
// store의 logged변수 값을 false
store.commit("setLogged", false);
}
else{
// stoer의 mutation을 호출하는 경우
store.commit("setLogged", true);
// stoer의 actions를 호출할 경우
store.dispatch("handleData", {});
}
})
// 메뉴를 클릭했을 떄
// store값 변경하기
const handleSelect = (idx) =>{
console.log(idx);
// 메소드명,value 값
store.commit("setMenu", idx);
}
// // store의 state 변수가 변경되는 시점을 바로 알 수 있음.
// store.subscribe((mutation, state) => {
// console.log('store.subscribe',mutation,state);
// })
return {state, menu, logged, handleSelect, uid, uname, urole}
}
}
</script>
<style lang="scss" scoped>
</style>
===========================================ファイル名src/stores/index.js
// npm install vuex@next --save
// 파일명 : src/stores/index.js
import {createStore} from "vuex";
import axios from 'axios';
// 모든 컴포넌트에서 공통으로 사용할 변수 설정
// props 와 emit 를 여기에서 처리함
const stores = createStore({
// 공통 상태 변수
state : {
menu : sessionStorage.getItem("CURL"), // 선택된 메뉴
logged : false, // 로그인 상태
uid : '', // 로그인한 사용자의 이메일정보
uname : '', // 로그인한 사용자의 이름
token : '', // 토큰을 저장소에 보관하지 않고 사용
role : '', // 로그인한 사용자의 권한을 저장
},
//가져가기 (getter)
getters : {
getMenu(state){
return state.menu;
},
getLogged(state){
return state.logged;
},
getUid(state) {
return state.uid;
},
getUname(state) {
return state.uname;
},
getUrole(state) {
return state.urole;
},
},
// 변경하기 (mutation) : 즉시변경
mutations:{
setMenu(state,value){
state.menu = value;
},
setLogged(state,value){
state.logged = value;
},
setUid(state, val) {
state.uid = val;
},
setUname(state, val) {
state.uname = val;
},
setUrole(state, val) {
state.urole = val;
},
},
// 변경하기(action) : 기다려야 되는 상황, 벡엔드 연동이 필요할 경우
actions:{
async handleData(context, payload){
console.log(payload);
const token = sessionStorage.getItem("TOKEN");
if(typeof token !== 'undefined'&& token !== null){
const url = `/member/validation`;
const headers = {"Content-Type":"application/json", "token":token };
const response = await axios.get(url, {headers:headers});
if(response.data.status === 200){
// mutations의 setUid, setUname을 호출해서 내용변경
context.commit("setUid", response.data.uid);
context.commit("setUname", response.data.uname);
context.commit("setUrole", response.data.urole);
context.commit("setLogged", true);
}
else{
// 토큰의 유효성을 검사하여 통과하지 못할경우
context.commit("setLogged", false);
}
}
}
},
});
export default stores;
===========================================ファイル名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;
req.body.urole = user.urole;
// 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:'토큰 오류'});
}
}
}
Reference
この問題について(node 2022/02/18), 我々は、より多くの情報をここで見つけました https://velog.io/@anrkfl/node-20220218テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol