Node.js Passport 모듈을 이용한 로그인 구현
2018-06-15 14:11:12

저번 글에 이어서 작성

로그인 구현이 어렵진 않지만 은근 귀찮은 작업이라.. 패스포트 모듈을 쓰면 쪼금 편한 거 같은? 느낌이다.

해야할 작업은 로그인을 한 이후 유저 세션을 레디스에 넣어서 쓰도록 하자. (이전 글에선 귀찬아서 mongo에 넣엇음)

먼저 필요한 모듈을 설치하자 아 난 요즘 yarn을 쓰고 잇음.

1
yarn add passport passport-local cookie-parser express-session redis connect-redis

이제 로그인을 구현 해보자.

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
const passport = require('passport');

const LocalStrategy = require('passport-local').Strategy;

// local Strategy 세팅
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true,
}, (req, email, password, done) => {
UserModel.findOne({
email: security.xssFilter(email),
password: security.changeHash(password),
}, (err, data) => {
if (err) { return done(err); }
if (!data) {
return done(null, false, 'Either no registered email address or the password is incorrect.');
}
return done(null, data);
});
}));

// TODO: 로그인 처리.
// eslint-disable-next-line no-unused-vars
router.post('/signIn', security.csrfProtection(), (req, res, next) => {
// eslint-disable-next-line consistent-return
passport.authenticate('local', (err, user, message) => {
// console.log('user', user);
// console.log('message', message);
if (err) { return res.status(500).json(err); }
if (!user) { return res.status(200).json({ result: false, title: 'error', comment: message }); }
req.logIn(user, (sessionErr) => { // session init
if (sessionErr) { return next(sessionErr); }
return res.status(200).json({ result: true, title: 'success', comment: 'login complete' });
});
})(req, res, next);
});

// TODO: profile 페이지
router.get('/success', security.isLogin, (req, res) => {
// console.log('req.user', req.user);
res.render('profile', {
profile: req.user,
});
});

passport.serializeUser((user, done) => {
done(null, user);
});

passport.deserializeUser((user, done) => {
const data = user;
delete data.password;
done(null, data);
});

위에서 순서대로 설명

LocalStrategy가 로그인 구현 부분의 핵심이다.

usernameField, passwordField는 내가 지은 게 아니고 모듈에 정의된 프로퍼티이다.

여기에 form이나 ajax로 데이터 보내는 데이터의 ID 파라미터 이름을 적어두면 된다.

실제 내 HTML에서도 위의 정의한 대로 되어 있음.

1
2
<input type="email" name="email" placeholder="Email" required />
<input type="password" name="password" placeholder="Password" required />

passReqToCallback는 아래의 콜백 데이터로 받을 것인가 그런건데 항상 true로 써 왓음. 딱히 false로 해보진 않앗다.

이제 이메일, 비밀번호가 클라이언트에서 보내주면 콜백 함수에서 email, password 여기에 넘어 온다.

그럼 db에서 (이전 글에 이어 mongodb) 이메일, 비밀번호로 조회 해서 (xss, hash는 이전 글에서 설명 햇으니 패스.) 나온 결과에 맞게 done()을 호출을 한다.

done에 몇개 까지 넣을 수 잇는지는 안 해봣는데 보통 첫 칸이 에러, 2번째 칸이 유저 데이터, 3번째 칸이 넣고 싶은 메세지 정도로 쓴다.

그럼 이 done이 어디로 가냐면 passport.serializeUser() 여기로 넘어 간다. 여기가 로그인 후 세션을 저장 하는 부분이다.

passport.deserializeUser()는 이제 웹 페이지 이동 시 호출 되서 세션에 담은 데이터를 호출 할수 잇게 해 줄 수 잇다.

아래에 /success로 가는 페이지에 req.user가 유저의 세션 데이터를 불러오는 것. (내가 지은게 아니고 모듈에서 지정한 이름)

모듈 만든 사람이 serializeUser에서 가볍게 유저 ID나 키만 저장하고 deserializeUser에서는 serializeUser에 저장한 유저 ID나 키로 DB에서 유저 데이터 조회 후 새로 세션을 갱신하라고 추천하는데 지금은 규모가 작으니 패스.

마지막으로 로그인할 때 처리할 라우터에 passport.authenticate를 적어서 우리가 작성한 passport Strategy를 사용 하게 끔 하면 된다.

req.logIn은 내가 만든 게 아니고 모듈에서 지원하는 건데 세션 적용을 하는 부분이다.

단순하게 form으로 데이터 처리 시에는 passport.authenticate를 미들웨어로 넣어서 처리하면 되는데 위의 사례는 로그인을 ajax로 처리할 때에 적용하는 패스포트 방법이다. 그리고 success로 가는 페이지에 security.isLogin 이 부분에 대해 적자.

1
2
3
4
5
6
7
// TODO: 로그인 세션 체크.
module.exports.isLogin = (req, res, next) => {
if (req.isAuthenticated()) {
return next();
}
return res.redirect('/');
};

유저 로그인 세션 체크하는 방법이다. 패스포트에서 지원함.

그리고 이게 끝이 아니고 몇 가지 자질구레한 설정을 express에 좀 해줘야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const cookieParser = require('cookie-parser');
const passport = require('passport');
const session = require('express-session');
const redis = require('redis');
const RedisStore = require('connect-redis')(session);
app.use(cookieParser()); // 쿠키 사용
app.use(session({ // 세션 적용
secret: 'hi?delryn', // 암호화를 하기위한 salt
resave: false, // 세션 재저장 여부 체크
saveUninitialized: false, // 초기화 되지 않은 세션 저장 여부 체크
store: new RedisStore({ // 세션 정보를 redis 저장
client: redis.createClient(config.redis),
host: config.redis.host,
port: config.redis.port,
prefix: 'session:',
db: 0,
}),
}));
app.use(passport.initialize()); // 앱 전역에 passport init
app.use(passport.session()); // 앱 전역에 passport 세션 사용

세션과 패스포트를 사용하기 위한 적용이다.

세션 또한 쿠키 기반이기 때문에 cookieParser라는 모듈을 써야 하고 express-session가 이제 세션 설정하는 코드다.

connect-redis는 세션 데이터를 서버 메모리가 아닌 redis에 저장하기 쉽게 해주는 모듈이다.

마지막으로 로그인 하는 짤

로그인

다음 글에서는 소셜로 로그인하는 법을 적어야 겟다.

참고