일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 국비지원
- git
- computerscience
- 야놀자
- 부트캠프
- 자바스크립트
- CSS
- 컴퓨터과학
- Javascript
- BFS
- html/css/js
- 프론트엔드개발자
- nodejs
- js
- LinkSnap
- 패스트캠퍼스
- 컴퓨터공학
- 호이스팅
- 코딩테스트
- CS
- github
- 너비우선탐색
- 백준
- cpu
- DFS
- KAKAO
- 그리디
- 국비지원취업
- 코테
- 알고리즘
- Today
- Total
My Boundary As Much As I Experienced
express로 세션(session) 구현하기 (feat. passport, express-session) 본문
express로 세션(session) 구현하기 (feat. passport, express-session)
Bumang 2024. 8. 5. 23:36세션은 회원기능이 필요할 때 가장 기본은 할 수 있는 보장된 인증 방식이다.
node.js에서 이를 쉽게 구현하려면 passport와 express-session 등을 많이 사용한다.
설치
npm install express-session passport passport-local
Express-session
express-session은 Express 애플리케이션에서 세션 관리를 쉽게 할 수 있도록 도와주는 미들웨어이다.
서버에서 각 사용자별로 고유한 세션을 생성하고, 이 세션에 데이터를 저장할 수 있게 한다.
세션ID를 클라이언트에 쿠키로 저장하여 서버와 클라이언트 간의 세션을 연결한다.
const session = require("express-session");
app.use(
session({
secret: "암호화에 쓸 비번",
resave: false, // 유저가 서버로 요청할 때마다 세션을 갱신할건지?
saveUninitialized: false, // 로그인 안 해도 세션을 만들건지?
cookie: {
maxAge: 60 * 60 * 1000 * 24 * 7,
},
})
);
passport 초기 설정
passport는 회원인증 도와주는 메인라이브러리이다.
그중 passport-local은 passport 중에서도 '아이디/비번 방식 회원인증'을 구현하는 라이브러리이다.
두 개 다 설치해줘야 세션을 구현할 수 있다.
일단 초기화는 LocalStrategy라는 객체를 생성하여 콜백 안에 로그인 로직을 전달해주면 된다.
const passport = require("passport");
const LocalStrategy = require("passport-local");
app.use(passport.initialize()); // 초기화
app.use(passport.session()); // 세션용 사용자 정보를 저장하는 미들웨어
// passport 내에 passport-local로 불러온 LocalStrategy를 생성하고
// 사용자 인증 로직을 구현한다.
passport.use(
new LocalStrategy(async (userId, password, cb) => {
// db조회해서 유저를 찾아온다.
let result = await db.collection("user").findOne({ username: userId });
// 유저 조회 결과가 없다면
if (!result) {
// 콜백을 리턴. (꼭 이름이 'done'이 아니어도 된다.)
return done(null, false, { message: "아이디 DB에 없음" });
}
// 유저는 존재하는데 password가 같다면
if (result.password == password) {
return done(null, result);
// 유저는 존재하는데 password가 다르다면
} else {
return done(null, false, { message: "비번불일치" });
}
})
);
// done (
// 에러여부: {...} | null,
// 인증여부: boolean, 에러 시 false
// 추가메시지: { message: ... }
// )
직렬화와 역직렬화
직렬화:
직렬화는 예전 포스팅에서 한 번 다뤘다. 객체나 복잡한 구조의 데이터를 비트의 연속으로 된 원시값으로 치환하는 것이다.
JSON.stringify()로 객체를 string화 하는 것을 떠올리면 된다.
여기 passport라이브러리에서 직렬화란 DB에서 불러온 무거운 유저 정보 객체를 세션에 다 넣지 않고
정말 필요한 것만 추려 '세션 객체'에 저장하는 과정이다.
사용자가 로그인 폼을 제출하고 인증에 성공하면, serializeUser 함수가 호출되어 사용자 정보를 세션에 저장한다.
즉, 직렬화는 로그인 성공 시 실행된다.
// 직렬화
passport.serializeUser((user, done) => {
// 내부 코드를 비동기적으로 실행(async/await과 비슷)
process.nextTick(() => {
done(
null,
//세션 document에 기록할 내용
{
id: user._id,
username: user.username,
}
);
});
});
직렬화가 이루어지면 세션에 아래처럼 유저정보가 등록된다고 생각하면 된다.
// 세션에 저장되는 정보
session: {
passport: {
user: 123
}
}
역직렬화:
역직렬화는 각 요청마다 세션에서 사용자 정보를 복원할 때 실행된다.
클라이언트가 서버에 요청을 보낼 때마다, Passport는 세션에서 저장된 사용자 ID를 사용하여 deserializeUser를 호출한다.
아래 예시에서 DB조회해서 가져온 유저 객체에서 password부분은 제거한 채로 라우트에 전달해준다.
보통 password같이 민감한 정보는 될 수 있으면(꼭 필요한게 아니라면) 조회 후 바로 지워준다고 하더라. (확실치 않음)
// 역직렬화
passport.deserializeUser(async (user, done) => {
let result = await db
.collection("user")
.findOne({ _id: new ObjectId(user.id) });
delete result.password;
process.nextTick(() => {
// 실제 최신 유저 or null이 담김
done(null, result);
});
});
로그인 시 passport에 유저정보를 전달하는 코드
앞서 말했듯 직렬화가 이루어지기 전에 로그인 시도를 구현하는 부분이다.
passport.authenticate(...)를 실행하는 것 같다.
app.post("/login", async (req, res, next) => {
// 3개의 인자를 콜백에 담을 수 있음
// error: 말그대로 에러가 있을 시 오는 객체
// user: 성공 시 유저 정보
// info: 실패 시 이유
passport.authenticate("local", (error, user, info) => {
if (error) {
// db조회 실패 및 서버 에러 시
return res.status(500).json(error);
} else if (!user) {
// user 조회는 했으나 정보가 다를 시
return res.status(401).json(info.message);
}
// 성공 시
req.logIn(user, (err) => {
if (err) return next(err);
res.redirect("/");
});
})(req, res, next);
// passport 미들웨어는 요청, 응답, next를 모두 받아서 처리함
});
역직렬화된 데이터를 각 라우트에서 활용하는 법
req.user 혹은 req.usAuthenticated()를 통해 역직렬화된 세션 유저데이터를 활용할 수 있다.
인증 여부를 아래처럼 req.usAuthenticated()를 통해 알 수도 있고,
req.user.username 등 user 객체에 있는 정보들을 활용할 수 있다.
// 프로필 라우트
app.get('/profile', (req, res) => {
if (req.isAuthenticated()) {
res.send(`Hello ${req.user.username}, your email is ${req.user.email}`);
} else {
res.redirect('/login');
}
});
'BackEnd > Node.js' 카테고리의 다른 글
express로 회원가입 API 만들기 (+ Hashing 적용하기, 접근 권한 적용하기) (0) | 2024.08.11 |
---|---|
직렬화(serialization)와 역직렬화(deserialization) (0) | 2024.08.07 |
Session, JWT, OAuth이란? (0) | 2024.08.05 |
express로 페이지네이션 구현하기 (0) | 2024.08.03 |
Express, ejs탬플릿과 MongoDB으로 게시판 CRUD 구현 (0) | 2024.07.29 |