passport 모듈을 활용해 id와 passport를 이용한 기본적인 인증 방식을 구현해보자.
Passport 모듈
추후에 SSO를 사용하여 사용자 로그인을 구현할 예정이므로 Passport 모듈을 이용해 로그인 기능을 구현해보겠습니다.
모듈 설치 및 연결
- 설치
1
npm i passport passport-local
app.js
파일에 passport 모듈을 연결해줍니다.
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
...
import sessionMiddleware from "./src/config/session.js";
import passport from "passport";
import passportConfig from "./src/config/passport/index.js";
const app = express();
const __dirname = path.resolve();
...
// req 객체에 passport 설정을 입력
app.use(passport.initialize());
passportConfig();
app.use(sessionMiddleware);
app.use(passport.session()); // req.session 객체에 passport정보를 추가 저장
// express-session에 의존하므로 뒤에 작성합니다.
// passport.session()이 실행되면, 세션쿠키 정보를 바탕으로 해서 passport/index.js의 deserializeUser()가 실행됩니다.
...
config/passport 작성
먼저 config/passport/index.js
에 serializeUser와 deserializeUser를 작성합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// config/passport/index.js
import passport from "passport";
import local from "./localStrategy.js";
import UserStorage from "../../models/model_user.js";
export default () => {
passport.serializeUser((req, user, done) => {
done(null, user.id);
// 자원낭비를 줄이기 위해 id만 저장합니다.
});
passport.deserializeUser(async (req, id, done) => {
try {
const user = await UserStorage.getUserInfo(id);
done(null, user);
} catch (err) {
done(err);
}
});
local();
};
다음 localStrategy.js
파일에 id + password로 인증하는 전략을 작성합니다.
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
// config/passport/localStrategy.js
import passport from "passport";
import { Strategy as LocalStrategy } from "passport-local";
import { userPasswordVerify } from "../utils/crypto_util.js";
import UserStorage from "../models/model_user.js";
export default () => {
passport.use(
"local",
new LocalStrategy(
// passport는 기본적으로 username와 password를 req에서 가져와 비교한다.
// 그 설정을 바꾸기 위해서 new LocalStrategy의 첫번째 인자로
// usernameField: '아이디로 원하는 이름',
// passwordField: '비밀번호로 원하는 이름'
{
usernameField: "id",
passwordField: "password",
// session: true, // 세션에 저장 여부
// passReqToCallback: false,
},
async (id, password, done) => {
try {
let user = await UserStorage.getUserInfo(id);
if (!user) {
return done(null, false, {
success: false,
msg: "가입되지 않은 회원입니다.",
});
}
let checkedPassword = await userPasswordVerify(
password,
user.password
);
if (checkedPassword) {
return done(null, user);
}
done(null, false, {
success: false,
msg: "비밀번호가 틀렸습니다.",
});
} catch (err) {
done(err);
}
}
)
);
};
Route 작성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import express from "express";
import ctrl from "../controllers/controller_user.js";
import validator_user from "../middleware/validator_user.js";
const Router = express.Router();
Router.get("/info", ctrl.output.info);
Router.get("/login", ctrl.output.login);
Router.get("/register", ctrl.output.register);
Router.get("/logout", ctrl.process.logout);
Router.post("/login", validator_user.login, ctrl.process.login);
Router.post("/register", validator_user.register, ctrl.process.register);
export default Router;
Controller 파일 수정
기존에 Controller에서 if 문과 response 객체를 활용해 인증했던 작업을 Passport 모듈을 이용해서 작성합니다.
Controller가 조금 더 간결한 코드로 작성된 것을 확인할 수 있습니다.
(크게 차이는 없지만 추가로 카카오, 네이버 등으로 로그인 기능을 확장이 쉬운 장점이 있습니다.)
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
import passport from "passport";
import {
createPasswordAndSalt,
} from "../middleware/crypto.js";
import UserStorage from "../models/model_user.js";
const output = {
info: (req, res) => {
res.render("user/info", {
// 저장된 세션으로 로그인 된 상태인지 확인
name: req.session.isLogined ? req.session.name : null,
});
},
login: ...,
register: ...,
};
const process = {
login: (req, res, next) => {
passport.authenticate("local", (err, user, response) => {
if (err) return next(err);
if (!user) return res.json(response);
return req.login(user, { session: false }, async (loginErr) => {
if (loginErr) return next(loginErr);
// 로그인 성공 했을 때
req.session.isLogined = true;
req.session.name = user.name;
return res.json({ success: true });
});
})(req, res, next); // 미들웨어 내의 미들웨어에는 (req, res, next)를 붙입니다.
},
register: ...,
logout: ...,
};
export default { output, process };