[Node JS] 로그인 회원가입 로그아웃 구현 #2 / 회원가입 / hash / schema / bcrypt
Sign-Up / In / Out implementation
bcrypt
와mongoose
를 사용해 비밀번호를 암호화 하고MongoDB
에 유저 정보 저장.
pwc
는 비밀번호를 확인해야해서 일부러 아무처리 안하고 저장했습니다.
▼회원가입을 위한
server.js
전체 코드▼코드복사는 아래 코드로 해주세요! 나중에 설명할때 나오는 코드는 모듈이 포함되어있지 않습니다!
만약 에러가 난다면 해당 모듈을npm
명령어를 통해 설치해주시고import
해주세요
const express = require('express');
const app = express();
app.use(express.static(__dirname + ''));
// allows you to ejs view engine.
app.set('view engine', 'ejs');
// importing .env file
require('dotenv').config();
// importing body-parser to create bodyParser object
const bodyParser = require('body-parser');
// allows you to use req.body var when you use http post method.
app.use(bodyParser.urlencoded({ extended: true }));
// importing db function that connects with MongoDB.
const { db } = require('./module/db');
// importing user schema.
const User = require('./module/user');
// importing bcrypt moudle to encrypt user password.
const bcrypt = require('bcrypt');
// declaring saltRounds to decide cost factor of salt function.
const saltRounds = 10;
db();
app.get('/login', auth, function(req, res) {
const user = req.decoded;
if(user) {
return res.render('loggedin', {user:user.docs});
} else {
return res.sendFile(__dirname + '/login.html');
}
});
app.post('/login/:signUpid/:signUpaddress/:signUppw/:signUppwc', function(req, res, next) {
let user = new User(req.body);
if(user.pw!==user.pwc) {
return res.send('Your password and password confirmation have to be same.');
}
User.findOne({id:(user.id)}, function(err, docs) {
if(err) throw err;
else if(docs == null) { // Entered ID is available.
if(user.id&&user.pw&&user.pwc) { // adding a new account.
return next();
} else return res.send('Please enter all the blanks.');
}
else {
return res.send('Your entered ID already exists.');
}
});
});
app.post('/login/:signUpid/:signUpaddress/:signUppw/:signUppwc', function(req, res) {
let user = new User(req.body);
bcrypt.genSalt(saltRounds, function (err, salt) {
if (err) throw err;
bcrypt.hash(user.pw, salt, function (err, hash) {
if (err) throw err;
user.pw = hash;
user.save();
return res.send('You have just created your new account!');
})
})
});
2. 회원가입 구현
login.html
파일을 만든 후 위의 폼을 만들기 위해 아래의 코드를 작성했다.
submit
버튼을 누르면SignUpAjax()
함수가 그리고Log in
버튼을 누르면signInAjax()
함수가 실행되며 해당 미들웨어로 데이터가 전송된다.
<form id="signup">
<h1>Sign up example</h1>
<div class="field">
<label for="id">ID:</label>
<input type="text" id="id" name="id" placeholder="Enter your fullname" /><br><br>
</div>
<div class="field">
<label for="pw">Address:</label>
<input type="text" id="address" name="address" placeholder="Enter your address" /><br><br>
</div>
<div class="field">
<label for="pw">PW:</label>
<input type="text" id="pw" name="pw" placeholder="Enter your password" /><br><br>
</div>
<div class="field">
<label for="pwc">PW confirmation:</label>
<input type="text" id="pwc" name="pwc" placeholder="PW confirmation" /><br><br>
</div>
<button type="button" onclick="SignUpAjax()">Submit</button><br><br>
</form>
<form id="singin">
<h1>Sign in example</h1>
<div class="field">
<label for="signinID">ID:</label>
<input type="text" id="signinID" name="signinID" placeholder="Enter your fullname" /><br><br>
</div>
<div class="field">
<label for="signinPW">PW:</label>
<input type="text" id="signinPW" name="signinPW" placeholder="Enter your password" /><br><br>
</div>
<button type="button" onclick="signInAjax()">Log in</button><br><br>
</form>
signUpAjax()
함수의 내용은 다음과 같다.
함수가 실행되면 폼의 데이터값을ajax
함수를 통해 미들웨어로 보낸다.
<script>
function SignUpAjax() {
const id = document.getElementById("id").value;
const address = document.getElementById("address").value;
const pw = document.getElementById("pw").value;
const pwc = document.getElementById("pwc").value;
document.getElementById("id").value = "";
document.getElementById("address").value = "";
document.getElementById("pw").value = "";
document.getElementById("pwc").value = "";
$.ajax({
type: "post",
url: 'http://localhost:8080/login/:signUpid/:signUpaddress/:signUppw/:signUppwc',
data: {id:id,address:address,pw:pw,pwc:pwc},
dataType:'text',
success: function(res) {
window.alert(res);
}
});
}
</script>
하지만 현재 이 웹사이트의 경우 경로는
/login
인데 로그인을 한 경우와 하지 않은 경우 둘로 나누면 로그인을 하지 않았을 경우 위의 만들어둔 폼을 보여주고 로그인을 한 경우는 유저가 로그인을 했음을 보여줘야한다. 그러므로server.js
에서 아래처럼 해주었다.
app.get('/login', auth, function(req, res) {
const user = req.decoded;
// user가 있으면 -> 로그인 한 경우 -> 유저정보를 함께 웹페이지와 보내준다.
if(user) {
return res.render('loggedin', {user:user.docs});
} else { // user가 없으면 -> 로그인 필요한 경우 -> 로그인 폼을 보내준다.
return res.sendFile(__dirname + '/login.html');
}
});
위의
auth
함수와const user = req.decoded
는 로그인 하는 부분에 나올 부분이므로 생략하겠습니다.
Schema
미들웨어로 호출되면
schema
를 사용해user
변수에 클라이언트 측으로부터 받은 데이터를 넣어준다. 아래와같이User.js
파일을module
폴더 안에다 만들었다. 나중에server.js
파일에서 사용할 수 있다. 객체의 변수 하나하나에 다르게 옵션을 설정할 수 있다.
// User.js
const mongoose = require('mongoose'); // declaring mongoose.
const userSchema = mongoose.Schema({ // making a schema called userSchema.
id: {
type: String,
maxLength: 50,
required: true,
unique: 1, // exists one unique value
},
address: {
type: String,
required: true,
maxLength: 100,
},
pw: {
type: String,
required: true,
maxLength: 100,
},
pwc: {
type: String,
required: true,
maxLength: 100,
},
});
const User = mongoose.model('User', userSchema);
module.exports = User; // exporting user schema.
아래와 같이
schema
형식의user.js
파일을server.js
파일에서 임포팅 해준 후ajax
통신으로 전달받은 데이터값들을user
변수에 넣어준다.나머지 부분은 주석에 설명하겠습니다.
코드는 맨 위의 전체코드에서 복붙하고 설명을 봐주세요 모듈과 함수들등은 아래에 따로 적지 않았습니다.
app.post('/login/:signUpid/:signUpaddress/:signUppw/:signUppwc', function(req, res, next) {
let user = new User(req.body);
// `pw`와 `pwc` 값을 비교후 다르다면 해당 문구를 클라이언트측으로 보내고 미들웨어를 종료한다.
if(user.pw!==user.pwc) {
return res.send('Your password and password confirmation have to be same.');
}
// `MongoDB`의 `findOne`메소드를 이용해 유저의 `id`를 기준으로 검색하고 `docs`변수에 리턴한다.
// `User.findOne`에서 앞의 `User`부분은 위에서 임포팅한 `schema`의 `User`이다.
User.findOne({id:(user.id)}, function(err, docs) {
if(err) throw err; // 에러 던지기
// `docs`값이 없다면 -> 중복되는 아이디가 없어서 사용 가능하다면!
else if(docs == null) { // Entered ID is available.
// 아이디, 비밀번호, 비밀번호확인이 모두 있는지 확인(빈값 확인)
if(user.id&&user.pw&&user.pwc) { // Enter adding a new account step.
// 미들웨어의 `next()`메소드.
// 미들웨어에서 next인자를 세번째에 선언해야 사용가능.
// 같은 POST타입의 같은 경로의 `바로 다음` 미들웨어로 이동한다.
return next();
} // 아이디, 비번, 또는 비번확인중 값이 비어있을때
else return res.send('Please enter all the blanks.');
}
else {// 리턴받은 docs의 값이 이미 존재해서 중복된 아이디일 경우.
return res.send('Your entered ID already exists.');
}
});
});
bcrypt & salt
아래는 위 미들웨어의 next()메소드로부터 호출받은 다음 미들웨어다. 위에서 이미 아이디를 만들 수 있는지 거쳤기때문에 아래 미들웨어가 실행되면 바로 만들면 된다.
‘bcrypt’모듈을 이용해 ‘salt’를 생성 후 비밀번호와 함께 해쉬함수에 인자로 넘겨줍니다. Salt를 생성하는 이유는 단순 해쉬만 이용하면 해쉬함수는 단방향의 특성을 가지기때문에, 특정 입력값은 항상 같은 결과값을 가지므로 보안에 취약합니다.Salt
라는 아주 작은 임의의 변수를 추가해 전혀 다른 해쉬출력값을 받도록 한 방법입니다.saltRounds
변수는salt
를 얼마나 복잡하게 만들었는지를 나타내는 인자로서 숫자가 높을수록 시간이 더 오래걸린며 1이 올라갈수록 시간은 두배 오래걸린다.
const bcrypt = require('bcrypt');
const saltRounds = 10;
app.post('/login/:signUpid/:signUpaddress/:signUppw/:signUppwc', function(req, res) {
// user의 정보 다시 넣기
let user = new User(req.body);
// salt 생성
bcrypt.genSalt(saltRounds, function (err, salt) {
if (err) throw err;
// 생성한 salt값을 hash함수에 비밀번호와 넣는다.
// 비밀번호는 해쉬화 되어서 hash변수로 리턴되어진다.
bcrypt.hash(user.pw, salt, function (err, hash) {
if (err) throw err;
// user.pw 에 리턴된 hash 넣기.
user.pw = hash;
// MongoDB에 저장.
user.save();
return res.send('You have just created your new account!');
})
})
});
References:
- https://mongoosejs.com/docs/queries.html
- https://expressjs.com/en/guide/writing-middleware.html
- https://stackoverflow.com/questions/46693430/what-are-salt-rounds-and-how-are-salts-stored-in-bcrypt
login.html
파일에서ajax
함수의success
부분에서 결과를 받은 후 이런식으로 알림이 가도록 할 수 있다.
success: function(res) {
window.alert(res);
}
MongoDB
에 저장된 모습.
Author And Source
이 문제에 관하여([Node JS] 로그인 회원가입 로그아웃 구현 #2 / 회원가입 / hash / schema / bcrypt), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@kon6443/Node-JS-로그인-회원가입-로그아웃-구현-2-회원가입저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)