Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User #9

Open
wants to merge 35 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
021d1d9
add gitignore
zeze1004 Jan 31, 2021
92c735c
제제 노빠구 킵고잉
zeze1004 Feb 7, 2021
c62ee58
user api
zeze1004 Feb 8, 2021
ba99b27
commit
zeze1004 Feb 8, 2021
22ac773
validation error 수정 중
zeze1004 Feb 8, 2021
4e69898
db 연결 오류
zeze1004 Feb 8, 2021
6a61244
email, password db 저장 구현
zeze1004 Feb 8, 2021
aa969d6
db에 이미 있는 email 입력시 '중복된 이메일입니다.' 표시
zeze1004 Feb 8, 2021
8705516
DB에 있는 email, password 입력하지 않ㅇ르 시 경고문 띄우기
zeze1004 Feb 8, 2021
4c09f1d
토큰 인증 함수 구현 중
zeze1004 Feb 8, 2021
21aa626
주석, 필요없는 api 삭제
zeze1004 Feb 9, 2021
e02f178
불필요한 npm package 제거
zeze1004 Feb 9, 2021
1001394
필요없는 주석 제거
zeze1004 Feb 12, 2021
965b0b7
비번 해시함수 적용 저장
zeze1004 Feb 12, 2021
0d2d206
필요없는 주석 제거
zeze1004 Feb 12, 2021
b73c978
user 모델에 계정 생성 시간 추가
zeze1004 Feb 12, 2021
a49294c
로그인 구현
zeze1004 Feb 12, 2021
6683c5b
로그인시 토큰 쿠키에 저장
zeze1004 Feb 12, 2021
40f5ecd
[model] token을 이용해 user 정보 가져오기 코드 추가
zeze1004 Feb 12, 2021
859513d
미들웨어 폴더 생성
zeze1004 Feb 12, 2021
91872f8
[auth] cookie-parser 없다는 오류 발생...
zeze1004 Feb 12, 2021
d98882b
app.cookiePaser 추가
zeze1004 Feb 12, 2021
2e985de
/auth 추가
zeze1004 Feb 12, 2021
821c7fc
/auth 추가
zeze1004 Feb 12, 2021
5a819c7
model에서 토큰 제거
zeze1004 Feb 13, 2021
b693bcd
불필요한 코드 삭제
zeze1004 Feb 13, 2021
3af7fa4
회원가입, 로그인 과정 셜명 md 파일 추가
zeze1004 Feb 13, 2021
c800ce5
불필요한 주석 제거
zeze1004 Feb 13, 2021
cdf5b67
모델에 토큰 다시 추가
zeze1004 Feb 13, 2021
c9a5c23
토큰 부여까지 작성
zeze1004 Feb 13, 2021
0523b6d
jwt 추가
zeze1004 Feb 13, 2021
a3c90b8
코드리뷰 받은 것 주석으로 추가
zeze1004 Feb 13, 2021
ad55003
오류 코드 삭제
zeze1004 Feb 14, 2021
fad2a71
prettier 버전 변경
zeze1004 Feb 14, 2021
e90bafd
불필요한 모듈 삭제
zeze1004 Feb 14, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@
"extends": ["plugin:prettier/recommended"],
"plugins": ["prettier"],
"rules": {
"prettier/prettier": "error"
"prettier/prettier": [
"error",
{
"endOfLine": "auto"
}
]
},
{
"useTabs": true,
"trailingComma": "es5",
"semi": true,
"singleQuote": true
}
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
db
.vscode1
15 changes: 0 additions & 15 deletions .vscode/settings.json

This file was deleted.

294 changes: 294 additions & 0 deletions User.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
# User

1. 회원가입 구현

```javascript
// router
// post로 클라이언트가 DB로 정보보내기
userRoutes.post("/signup", asyncWrapper(userController.userCreate));
```

```javascript
// model
const userSchema = new Schema({
email: {
type: String,
required: true,
unique: true,
trim: true,
},
password: {
type: String,
required: true,
},
// 항상 필요
createdAt: {
type: Date,
default: Date.now,
},
updatedAt: {

}
});
```

```javascript
// controller
// 회원가입 로직
// email, password 회원가입
import { userModel } from "./user.model";
import { StatusCodes, ReasonPhrases } from "http-status-codes";

const userController = {};

userController.userCreate = async (req, res) => {
try {
console.log(req.body);
const user = await userModel.create({
email: req.body.email,
password: req.body.password,
});
return res.json(user);
} catch (error) {
return res
.status(StatusCodes.INTERNAL_SERVER_ERROR)
.send("중복된 이메일입니다.");
}
};
```

mvc 패턴으로 간단하게 회원가입 로직을 구현했다.

모델에서 email을 unique라고 설정했기에 중복된 email 입력 시 회원가입 되지 않게 설정했다.

아직 프론트단이랑 합치지 않아서 에러시 res.send만 선언되게 작성했는데 어떻게 할 지 논의해봐야 한다.

사용자가 이메일, 비밀번호를 post하면 .create를 통해 DB에 "email", "password"에 저장됨

2. password 복호화하여 저장

평문 저장시 해킹 위험이 커 bcrypt 모듈을 이용해 복호화하여 저장

```javascript
import bcrypt from "bcrypt";
const saltRounds = 10;

// password 암호화
userSchema.pre("save", function (next) {
let user = this;

if (user.isModified("password")) {
bcrypt.genSalt(saltRounds, function (err, salt) {
if (err) return next(err);
bcrypt.hash(user.password, salt, function (err, hash) {
if (err) return next(err);
user.password = hash;
next();
});
});
} else next();
});
```

password가 저장되는 save 함수가 실행되기 전에 bcrypt 모듈을 이용해 password와 salt를 해싱하고 그 값을 password로 대체한다.

salt로 무작위 단어를 새로운 해시값을 추가로 password에 적용함



🍓이미 짠 해시함수에 소금을 뿌리자!🍓

비밀번호는 해시함수값으로 DB에 저장하는데

Rainbow Table에 해시값 적용하면 기존 비밀번호(plainPassword)를 알 수 있음



[리빙포인트]

Rainbow Table에는 흔히 쓰는 단어들의 해시값이 저장되었으므로 비밀번호 만들 때는 단어가 아닌 무작위 글씨, 특수기호 조합으로 만들자 ^^!



3. 로그인 구현

회원가입 된 유저가 email, password를 입력해 로그인을 시도하면

db에 해당 email이 있는지 먼저 검색 후

해싱된 password값이랑 일치 일치 되는 지 비교한다

```java script
// route
userRoutes.post('/signin', asyncWrapper(userController.login));
```

```javascript
// model
userSchema.methods.comparePassword = function (plainPassword) {
return bcrypt
.compare(plainPassword, this.password)
.then((isMatch) => isMatch)
.catch((err) => err);
};
```

```javascript
// 로그인시 DB에 저장된 email 먼저 찾고, email 있다면 암호화된 password랑 user가 입력한 password 비교
userController.login = async (req, res) => {
try {
const user = await userModel.findOne({
// email 먼저 비교
email: req.body.email
});
if (!user) {
return res
.status(StatusCodes.BAD_REQUEST)
.send('가입 되지 않은 회원입니다.');
// .redirect("/api-docs");
}
user
.comparePassword(req.body.password)
.then((isMatch) => {
// password 일치 안 할 시
if(!isMatch) {
return res.send('비밀번호가 일치하지 않습니다.');
}
});
// password 일치 시
res.send('로그인 되었습니다.');
})
} catch (error) {
return res
.status(StatusCodes.INTERNAL_SERVER_ERROR)
.json({ error: error.toString() });
}
};
```

.findOne을 통해 DB에서 email을 찾는다

email이 있을 시 model 파일에 작성된 comparePassword(plainPassword)가 실행된다.

입력 받은 req.body.password를 인자로 받은 comparePassword는

bcrypt.compare 함수를 통해 req.body.password와 해싱된 password를 비교한다.

일치 시 토큰을 user에게 부여하고 로그인 시킨다.

4. 토큰 부여

```javascript
// model
// 토큰 생성
userSchema.methods.generateToken = function () {
const token = jwt.sign(this._id.toHexString(), "zeze"); // secretKey
this.token = token;
return this.save().then((user) => user);
// .catch((err) => err);
};
```

`jwt.sign(payload, secretKey)`에서 payload는 string 형식이어야 한다.

그러나 mongodb에서 생성되는 \_id는 정수이므로 mongodb 함수인`.toHexString()`를 통해 형변환 해줘야 한다.

생성된 토큰은 DB에 저장해야 하므로 model에 token을 추가로 만들어준다.

```javascript
// model
const userSchema = new Schema({
...,
token: {
type: String
},
...
});
```

```javascript
// controller
userController.login = async (req, res) => {
try {
const user = await userModel.findOne({
// email 먼저 비교
email: req.body.email,
});
if (!user) {
return res
.status(StatusCodes.BAD_REQUEST)
.send("가입 되지 않은 회원입니다.");
}
user.comparePassword(req.body.password).then((isMatch) => {
// password 일치 안 할 시
if (!isMatch) {
return res.send("비밀번호가 일치하지 않습니다.");
}
});
// password 일치 시
user.generateToken().then((user) => {
res
.cookie("x_auth", user.token)
.status(200)
.send("로그인 되었습니다.");
});
} catch (error) {
return res
.status(StatusCodes.INTERNAL_SERVER_ERROR)
.json({ error: error.toString() });
}
};
```

다시 login cotroller로 돌아가서 password가 일치된 user에 한해서 token을 부여한다.

user.generateToken()을 통해 token을 만들고 cookie에 쿠키에 token을 저장하여

유저가 서버에 request할 때마다 서버는 token이 일치하는지만 검증하면 된다.



🍒JWT에 대해 알아보자🍒

Token Based Auth

![image-20210213171222116](C:\Project\DSC_web_backend\토큰1.png)

JWT

헤더: Algorithm, Token type

- 어떤 알고리즘으로 인크립션(암호화)할건지 결정

```json
{
"alg": "HS256",
"typ": "JWT"
}
```

페이로드: Data

- 개발자가 원하는 걸 저장하면 된다.

- 네트워크에 정보가 올라가므로 최소한의 데이터만 저장하는 걸 권장

```json
{
"id": "1223034", // 사용자 unique id
"exp": 21313, // 토큰 만료기간
...,
"CreditCardNum": 3242342 // 절대 X, 헤더와 페이로드는 암호화되지 않음
}
```

시그니쳐

- 헤더와 페이로드에 시크릿키를 추가한 채 저장

```json
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
ZEZE // SECRET KET
)
```
1 change: 1 addition & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ connectMongo().then(() => {
extended: true,
})
);

// Log everything at dev level.
app.use(morgan("dev"));
// Use main router.
Expand Down
Loading