회원 로그인 기능 구현
https://tlseoqja.tistory.com/53
[Node] 회원 로그인, 비밀번호 복호화 및 JWT 토큰 생성
비밀번호 암호화 https://tlseoqja.tistory.com/51 [Node] 회원 가입 시 비밀번호 암호화 MongoDB 연동 및 회원 가입 기능 구현 https://tlseoqja.tistory.com/49 [Node] node.js 서버와 MongoDB 연동하기 (mongoose) Mongoose란? Mong
tlseoqja.tistory.com
JWT 토큰 인증
토큰 인증은 개인 정보 보호를 위해 필수적으로 거쳐야하는 로직 중 하나이다.
서버로 부터 받아온 모든 개인 정보를 저장소에 저장하게 되면 개인 정보를 탈취당하기 쉬울 것이다.
그래서 암호화된 토큰을 저장해놓고 개인 정보가 필요한 페이지 접속 시 토큰으로 개인 정보를 받아오는 것이 좋다.
각 페이지마다 동일 함수를 적게되면 코드 관리가 어려워지니, middleware 폴더를 생성하고 함수를 작성하겠다.
middleware
프로젝트 루트 폴더 아래에 middleware 폴더를 생성하고 auth.js 파일을 생성해준다.
auth.js에선 쿠키에 저장된 JWT 토큰 값과 설정한 시크릿 키 값으로 복호화 후 토큰 일치 여부를 판단한다.
auth.js
// auth.js
import User from "../models/User.js";
const auth = (req, res, next) => {
// 인증 처리
// 클라이언트 쿠키에서 토큰을 가져온다.
const token = req.cookies.x_auth;
// 토큰을 복호화한 후 유저를 찾는다.
User.findByToken(token, (err, user) => {
if( err ) {
throw err;
}
// 유저가 없으면 인증 NO
if( !user ) {
return res.json({
isAuth: false,
error: true,
});
}
// 유저가 있으면 인증 OK
req.token = token;
req.user = user;
next();
});
};
export default auth;
User.js
auth.js에서 사용할 findByToken() 함수를 작성해준다.
// User.js
userSchema.methods.generateToken = function(cb) {
...
};
userSchema.statics.findByToken = function(token, cb) {
// jsonwebtoken을 이용해서 토큰 생성
const user = this;
// 토큰을 decode한다.
jwt.verify(token, "secretToken", function(err, decoded) {
// 유저 아이디를 이용해서 유저를 찾은 다음에
// 클라이언트에서 가져온 token과 DB에 보관된 토큰이 일치하는지 확인
user.findOne({ _id: decoded, token: token })
.then((info, err) => {
if( !info ) {
return cb(err);
}
cb(null, info);
})
});
};
findByToken 함수의 로직은 다음과 같다.
- 쿠키에서 받아온 token값과 설정한 시크릿 키 값으로 jwt의 verify 함수를 사용해서 토큰을 복호화한다.
- findOne함수를 사용해서 토큰을 생성할때 사용한 _id 값과 token 값으로 해당 유저의 실존 여부를 확인한다.
- 회원 정보가 없는 경우 콜백 함수에 err를 담아서 리턴하고, 정보가 있는 경우 회원 정보를 리턴한다.
static method vs instance method
findByToken과 위의 두 함수(comparePassword, generateToken)에는 차이가 있다.
바로 스키마의 methods로 선언됐는지 statics로 선언됐는지의 차이이다.
methods(instance method)는 모델을 통해 생성된 인스턴스에서 사용 가능한 메소드이고,
statics(static method)는 모델 자체에서 사용가능한 메소드이다.
// auth.js
import User from "../models/User.js";
// static method
User.findByToken(token, (err, user) => {
...
});
// index.js
import User from "./models/User.js";
User.findOne({ email: req.body.email })
.then((user) => {
...
// instance method
user.comparePassword(req.body.password, (err, isMatch) => {
...
});
});
});
위 코드에서 static method로 선언된 findByToken은 import한 User 모델에서 직접 사용됐다.
하지만 instance method로 선언된 comparePassword는 Response로 받아온 user 인스턴스에서 사용됐다.
또 다른 차이점은 this 바인딩의 차이이다.
methods로 함수를 선언하면, 함수를 호출한 객체 자체가 this가 된다.
하지만 statics으로 함수를 선언하면, 모델 그 자체가 this가 된다.
// methods
userSchema.methods.generateToken = function(cb) {
const user = this;
console.log(this);
// 결과
// {
// name: "eoqna",
// email: "eoqna@naver.com",
// _id: " ~ ",
// ...
// }
};
// statics
userSchema.statics.findByToken = function(token, cb) {
const user = this;
console.log(this);
// 결과
// Model{ User }
};
index.js
위 두 함수를 사용해서 인증 API를 생성한다.
...
import auth from "./middleware/auth.js";
...
app.post("/api/users/login", (req, res) => {
...
});
app.get("/api/users/auth", auth, (req, res) => {
// 여기까지 미들웨어를 통과했다는 얘기는 Authentication 이 true라는 말
res.status(200).json({
_id: req.user._id,
isAdmin: req.user.role === 0 ? false : true,
isAuth: true,
email: req.user.email,
name: req.user.name,
lastname: req.user.lastname,
role: req.user.role,
image: req.user.image,
})
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
테스트
우선 포스트맨을 통해 미로그인(토큰 X) 시 테스트를 진행한다.
결과는 다음과 같다.
이번엔 로그인 진행 후 쿠키에 JWT 토큰 값이 있는 경 동일한 API로 테스트를 진행한다.
참고 사이트
[Mongoose] mongoose 에서 statics method와 instance method의 차이점
이 글에서는 우리가 Mongoose를 사용할때 정의하는 static과 method의 차이점에 대해서 서술한다. 사용되는 곳의 차이점 instance method는 모델을 통해 생성된 인스턴스에서 사용가능한 메서드이고, static
systorage.tistory.com
'🖥️Frontend > Node' 카테고리의 다른 글
[Node] 로그아웃 기능 구현 (0) | 2023.12.01 |
---|---|
[Node] 회원 로그인, 비밀번호 복호화 및 JWT 토큰 생성 (0) | 2023.11.29 |
[Node] 회원 가입, 비밀번호 암호화 (2) | 2023.11.28 |
[Node] nodemon 설치 (자동 코드 반영) (1) | 2023.11.28 |
[Node] node.js 서버와 MongoDB 연동하기 (mongoose) (2) | 2023.11.23 |
댓글