1. passport 모듈
: 노드에서 사용할 수 있는 사용자 인증 모듈이다.
사용 방법이 간단할 뿐만 아니라, 사용자 인증 기능을 독립된 모듈 안에서 진행할 수 있도록 도와준다.
특히 익스프레스를 사용할 경우 미들웨어로 사용할 수 있어서 간단한 설정만으로 로그인 기능을 만들 수 있다.
2. 페이스북 개발자 센터
https://developers.facebook.com
3. 페이스북 개발자 센터에 앱 만들기
: 로그인 후 내 앱 클릭 > 앱 만들기 클릭
> 연결된 환경 구축 선택 후 계속 클릭(페이스북 로그인 사용만 해볼 예정) > 표시할 앱 이름 설정 후 앱 만들기 클릭
> 앱 만들기 완...!
4. 페이스북 로그인을 사용하기 위한 설정
: Facebook 로그인 설정 클릭 > www 웹 클릭
> 사이트 URL이 존재하지 않으므로 http://localhost:3000으로 설정 후 save 클릭
> FaceBook 로그인의 설정 클릭 > 클라이언트 OAuth 로그인, 웹 OAuth 로그인의 설정을 예로 변경
5. 코드와 연결해줄때, 앱 ID와 앱 시크릿 코드를 이용해야 한다.
6. 대부분 홈페이지를 만들 때, 사이트 서버와 DB 서버를 분리시키는 경우가 많다.
왜냐하면 사이트에 부하가 가면 DB도 느려질 수 있기 때문이다.
7. [참고] 자바스크립트에서 자바 스크립트를 부를때는 확장자를 쓰지 않는다.
8. [마무리 못한 부분은 다음시간에 계속할 예정] passport 모듈을 이용한 로컬 회원가입, 로그인
** 1_possport.js
const express = require('express');
const bodyParser = require('body-parser');
const logger = require('morgan');
const cookieParser = require('cookie-parser');
const expressSession = require('express-session');
const passport = require('passport'); // npm i passport
const static = require('serve-static');
const path = require('path');
const expressErrorHandler = require('express-error-handler'); // npm i express-error-handler
const app = express();
const router = express.Router();
app.use(cookieParser());
app.use(expressSession({
secret: '!@#$%^&^*&(',
resave: false,
saveUninitialized: true,
cookie: { maxAge: 60 * 60 * 1000 }
}));
app.use(logger('dev'));
// passport의 세션을 사용하려면 그 전에 express의 세션을 사용하는 코드가 먼저 나와야 한다.
app.use(passport.initialize()); // passport 초기화
app.use(passport.session());
app.use(bodyParser.urlencoded({extended: false}));
app.use('/public', static(path.join(__dirname, "public")));
app.use('/', router);
const errerHandler = expressErrorHandler({
static: {
'404': './public/404.html'
}
});
app.use(expressErrorHandler.httpError(404));
app.use(errerHandler);
app.set('views', __dirname + "/views");
app.set('view engine', 'ejs');
const config = require('./config/config');
const database = require('./database/database');
const configPassport = require('./config/passport');
configPassport(app, passport);
app.listen(config.server_port, () => {
console.log(`${config.server_port} 포트로 서버 실행 중...`);
database.init(app, config);
});
** /public/404.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>페이지를 찾을 수 없습니다.</title>
<style>
body { background-color: deepskyblue; }
h2 { font-size: 80px; }
p { font-size: 20px; }
h2, p { text-align: center; }
</style>
</head>
<body>
<h2>404</h2>
<p>요청한 페이지를 찾을 수 없습니다.</p>
</body>
</html>
** /config/config.js
module.exports = {
server_port: 3000,
db_url: 'mongodb://localhost:27017/frontenddb',
db_schemas: [{file:'./member_schema', collection:'member2', schemaName:'MemberSchema', modelName:'MemberModel'}],
facebook: {
clientID: '앱 ID',
clientSecret: '앱 시크릿 코드',
callbackURL: 'http://localhost:3000/auth/facebook/callback'
}
}
** /database/database.js
const mongoose = require('mongoose');
let database = {};
database.init = function(app, config) {
console.log('database init() 호출!');
connect(app, config);
}
function connect(app, config) {
console.log('connect() 호출!');
mongoose.Promise = global.Promise;
mongoose.connect(config.db_url);
database.db = mongoose.connection;
database.db.on('error', console.error.bind(console, 'mongoose connection error'));
database.db.on('open', () => {
console.log('데이터베이스 연결 성공');
createSchema(app, config);
});
}
function createSchema(app, config) {
const schemaLen = config.db_schemas.length;
console.log(`스키마의 개수 : ${schemaLen}`);
for(let i=0; i<schemaLen; i++) {
/*
{
file: './member_schema',
collection: 'member2',
schemaName: 'MemberSchema',
modelName: 'MemberModel'
}
*/
let curItem = config.db_schemas[i];
let curSchema = require(curItem.file).createSchema(mongoose);
console.log(`${curItem.file} 모듈을 불러온 후 스키마를 정의함`);
let curModel = mongoose.model(curItem.collection, curSchema);
console.log(`${curSchema.collection} 컬렉션을 위해 모델을 정의함`);
database[curItem.schemaName] = curSchema; // database[MemberSchema]
database[curItem.modelName] = curModel; // database[MemberModel]
console.log(`스키마이름[${curItem.schemaName}], 모델이름[${curItem.modelName}]이 데이터베이스 객체의 속성으로 추가되었습니다.`)
app.set('database', database);
console.log('database 객체가 app 객체의 속성으로 추가됨');
}
}
module.exports = database;
** /database/member_schema.js
const { Schema } = require('mongoose');
const crypto = require('crypto'); // npm i crypto
Schema.createSchema = function(mongoose) {
console.log('createSchema() 호출!');
const MemberSchema = mongoose.Schema({
userid: {type:String, require:true, default: ''},
hashed_password: {type:String, default:''},
name: {type:String, default:''},
salt: {type:String},
age: {type:Number, default:0},
create_at: {type:Date, default:Date.now},
update_at: {type:Date, default:Date.now},
provider: {type:String, default:''},
authToken: {type:String, default:''},
facebook: {}
});
MemberSchema.virtual('userpw')
.set(function(userpw) {
this._userpw = userpw;
this.salt = this.makeSalt();
this.hashed_password = this.encryptPassword(userpw);
})
.get(function() {
return this._userpw;
});
MemberSchema.method('makeSalt', function() {
console.log('makeSalt() 호출!');
return Math.round((new Date().valueOf() * Math.random())) + ''; // 문자열
});
MemberSchema.method('encryptPassword', function(plainText, inSalt) {
if(inSalt) { // 로그인
return crypto.createHmac('sha1', inSalt).update(plainText).digest('hex');
} else { // 회원가입
// salt에 저장된 값을 가져와서 sha1 암호화를 통해 plainText를 섞어줌 -> 16진수로 변환
return crypto.createHmac('sha1', this.salt).update(plainText).digest('hex');
}
});
MemberSchema.method('authenticate', function(plainText, inSalt, hashed_password) {
if(inSalt) {
console.log('authenticate() 호출! <inSalt 있음>');
return this.encryptPassword(plainText, inSalt) == hashed_password;
} else {
console.log('authenticate() 호출! <inSalt 없음>');
return this.encryptPassword(plainText) == this.hashed_password;
}
});
// pre() : 특정 작업이 일어나기 전에 미리 호출되는 메소드이다.
// 트리거 역할
MemberSchema.pre('save', (next) => {
if(!this.isNew) return next();
if(!validatePresenceOf(this.userid)) {
next(new Error('유효하지 않은 password입니다.'));
} else {
next();
}
});
const validatePresenceOf = function(value) {
return value && value.length; // 데이터가 있는지 여부
}
console.log('MemberSchema 정의 완료!');
return MemberSchema;
}
module.exports = Schema;
** /config/passport.js
const local_signup = require('./passport/local_signup');
const local_login = require('./passport/local_login');
module.exports = function(app, passport) {
console.log('config/passport 호출!');
}
** /config/passport/local_signup.js
// passport 회원가입
// 1. 로컬 회원가입
// npm i passport-local
const LocalStrategy = require('passport-local').Strategy;
module.exports = new LocalStrategy({
usernameField: 'userid',
passwordField: 'userpw',
passReqToCallback: true
}, (req, userid, userpw, done) => {
const name = req.body.name;
const age = req.body.age;
console.log(`passport의 local-signup 호출 : userid=${userid}, userpw=${userpw}, name=${name}, age=${age}`);
// 실행문장에서 데이터가 blocking 되지 않도록 사용 => async 방식으로 변경
process.nextTick(() => {
let database = req.app.get('database');
database.MemberModel.findOne({userid:userid}, (err, user) => {
if(err) return done(err);
if(user) {
console.log('이미 가입된 계정입니다.')
return done(null, false);
} else {
let user = new database.MemberModel({userid:userid, userpw:userpw, name:name, age:age});
user.save((err) => {
if(err) throw err;
console.log('가입완료');
return done(null, user);
});
}
});
});
});
** /config/passport/local_login.js
// passport 로그인
// 1. 로컬 로그인
const LocalStrategy = require('passport-local').Strategy;
module.exports = new LocalStrategy({
usernameField: 'userid',
passwordField: 'userpw',
passReqToCallback: true
}, (req, userid, userpw, done) => {
console.log(`passport의 local-login 호출 : userid=${userid}, userpw=${userpw}`);
let database = req.app.get('database');
database.MemberModel.findOne({userid:userid}, (err, user) => {
if(err) return done(err);
if(!user) {
console.log('계정이 존재하지 않습니다.');
return done(null, false);
}
let authenticate = user.authenticate(userpw, user.salt, user.hashed_password);
if(!authenticate) {
console.log('비밀번호가 일치하지 않습니다.')
return done(null, false);
}
return done(null, user);
});
});
'웹_프론트_백엔드 > 프론트엔드' 카테고리의 다른 글
2021.03.20 (0) | 2021.04.20 |
---|---|
2021.03.14 (0) | 2021.04.18 |
2021.03.07 (0) | 2021.04.02 |
2021.03.06 (0) | 2021.03.14 |
2021.02.28 (0) | 2021.03.01 |