app.use(passport.initialize())
app.use(session({
secret: `${password}`,
resave : false,
saveUninitialized : false,
cookie : { maxAge : 60 * 60 * 1000 },
store : MongoStore.create({
mongoUrl : `mongodb+srv://sikim0721:${password}@cluster0.tqbj5n0.mongodb.net/?retryWrites=true&w=majority`,
dbName : "forum"
})
}))
app.use(passport.session())
- passport 초기화를 통해 passport라이브러리를 사용할 수 있도록 한다.
- 이제 세션을 사용해서 사용자가 로그인 할 때 세션을 설정할 수 있다. resave : false는 세션을 중간에 다시 저장하지 않도록 한다.
- saveUninitialized는 초기화되지 않은 세션은 저장하지 않도록 한다.
- cookie는 클라이언트가 세션으로 들어왔을 때 받는 입장권으로, ms를 기준으로 60 * 1000 = 60,000ms = 1분동안 쿠키에 user.id를 저장한다.
- store는 connect-mongo 모듈을 이용하는 것인데, 세션 데이터를 저장할 때 사용한다. 사용자가 로그인 하면 자동으로 MongoDB에 Session 칸이 생기는데 여기에 id, pw가 해싱 되어서 session에 저장된다.
- passport.session()은 사용자의 세션을 지속적으로 관리하고, 정보를 유지한다. 이 메소드가 있어야지 사용자가 로그인 된 상태를 유지할 수 있다.
passport.use(new LocalStrategy(async (user_id, user_pw, cb) => {
let user = await db.collection('user').findOne({ username : user_id });
if (!user) {
return cb(null, false, { message: '아이디 DB에 없음' });
}
if (await bcrypt.compare(user_pw, user.password)) {
return cb(null, user);
} else {
console.log(user_id); // 사용자가 제공한 아이디 출력
return cb(null, false, { message: '비번불일치' });
}
}))
- 사용자 로그인 인증 함수로서, 라이브러리 사용법에 있으니 외워두지 말고 사용법을 검색해서 사용하자.
- DB에서 user_id와 일치하는 정보를 찾고, user라는 변수에 저장한다. 이 유저가 없다면 false 메세지를 findOne()에 전송하고 메세지를 출력한다.
- bcrypt는 해싱 함수로서, 유저가 입력한 user.pw와 db에 저장된 비밀번호를 cmp하며, 일치하면 user를 리턴하고 일치하지 않으면 사용자가 제공한 아이디를 출력하고 일치하지 않는다고 에러 메세지를 전송한다.
passport.serializeUser((user, done) => {
console.log(user)
process.nextTick(() => {
done(null, { id : user._id, username : user.username })
})
})
// 쿠키 분석하는 역할
passport.deserializeUser(async (user, done) => {
let result = await db.collection('user').findOne({ _id : new ObjectId(user.id) })
if(result) {
delete result.password
process.nextTick(() => {
done(null, result)
})
} else {
done(new Error('User not found'))
}
})
- serializeUser는 사용자 객체를 세션에 저장할 때 호출하는 함수로, console로 user객체를 출력하고, nextTick()을 사용해서 다음 이벤트루프에서 done을 호출해 작업이 완료된것을 전달한다. 그리고 세션에 저장할 사용자 정보를 JSON으로 전달한다.
- deserializeUser는 사용자 객체를 복원, 분석할 때 사용하는 함수로, 세션에 저장된 사용자 id를 찾고, 있다면 객체를 반환, 없다면 error 메세지를 전송한다.
function loggedin(req, res, next) {
if (req.user) {
next()
} else {
res.send("로그인을 해주세요")
}
}
- 로그인을 하면 세션과 쿠키에 사용자의 정보가 등록이 되는데, 여기서는 쿠키에 사용자 정보가 들어왔단 것을 기준으로 req.user가 있다면 다음 미들웨어로 실행하고 없다면 로그인을 하라고 메세지를 전송한다.
app.post('/login', async (req, res, next) => {
try {
passport.authenticate('local', (err, user, info) => {
if (err) return res.status(500).json(err)
if (!user) return res.status(401).json(info.message)
req.logIn(user, (err)=> {
if(err) return next(err)
res.redirect('/')
})
})(req, res, next)
} catch(err) {
console.error(err)
}
})
- login페이지에서 서버로 데이터를 전송을 할 때, passport.authenticate()메소드를 사용해서 인증을 시도한다. 서버 에러가 발생하면 500 상태 코드를 전송하고, user가 없다면 401 상태코드를 전송한다.
- 유저가 인증되면 세션에 사용자 정보를 저장하고, '/'경로로 리다이렉션을 해준다.
app.post('/register', async(req, res) => {
let hashing = await bcrypt.hash(req.body.password, 10)
await db.collection('user').insertOne({ username : req.body.username, password : hashing })
res.redirect('/')
})
- '/register'페이지에서 사용자 등록 데이터를 전송하고 나면 bctypt라는 해싱 메소드를 이용해 req.body.password를 10번 꼬아서 hashing 변수에 저장한다.
- DB에서 username과 password에 정보를 저장하고, password : hashing을 통해 해시 함수로 넣은 비밀번호를 저장한다.