---
# System prepended metadata

title: "\U0001F3C5 Day 27 - 登入功能"
tags: [node.js, Node.js 直播班-2024春季班, login]

---

# 🏅 Day 27 - 登入功能
延續前一天，在註冊時將密碼加密後儲存至資料庫，當使用者要進行登入時我們就需要將密碼解密來比對與使用者輸入的密碼是否符合

**密碼解密**
`bcrypt.compare()`
```javascript
// 尋找資料庫符合接收到的使用者資料，因 password 欄位在 Schema 通常設定為不顯示，因此可使用 select() 將密碼顯示出來
const user = await User.findOne({ email }).select('+password'); // email 為使用者輸入時的信箱

bcrypt.compare(password, user.password);
// 第一個參數是接收到的密碼，第二個參數是由資料庫找出的該 user 的密碼，為雜湊加密的字串
```


### 參考資源
- [bcryptjs - compare](https://github.com/dcodeIO/bcrypt.js#compares-hash-callback-progresscallback)
- [mongoose select()](https://mongoosejs.com/docs/api/query.html#:~:text=When%20using%20string%20syntax%2C%20prefixing%20a%20path%20with%20%2D%20will%20flag%20that%20path%20as%20excluded.%20When%20a%20path%20does%20not%20have%20the%20%2D%20prefix%2C%20it%20is%20included.%20Lastly%2C%20if%20a%20path%20is%20prefixed%20with%20%2B%2C%20it%20forces%20inclusion%20of%20the%20path%2C%20which%20is%20useful%20for%20paths%20excluded%20at%20the%20schema%20level.)

題目(回報任務)
---
實做登入功能 POST `/users/sign_in`，依照下方需求完整程式碼：
- 若有 Email、Password 有欄位未填寫，使用 `appError()` 自訂錯誤訊息：`帳號密碼不可為空`
- 使用 bcryptjs `compare()` 比對密碼是否與資料庫中的密碼符合
- 若未比對成功，使用 `appError()` 自訂錯誤訊息：`帳號或密碼錯誤，請重新輸入！`

```javascript=
var express = require('express');
var router = express.Router();
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const handleErrorAsync = require('../service/handleErrorAsync.js');
const validator = require('validator');
const User = require('../models/users');
const appError = require('../service/appError.js');


router.post('/sign_in', handleErrorAsync(async (req, res, next) => {
  const { email, password } = req.body;
  
  if (!email) {
    return next(appError(400, "Email欄位不能為空值！", next));
  }
  if (!password) {
    return next(appError(400, "Password欄位不能為空值！", next));
  }
    
  const user = await User.findOne({ email }).select('+password');

  if (!user) {
    return next(appError(400, "使用者不存在", next));
  }

  const auth = await bcrypt.compare(password, user.password);
  if (!auth) {
    return next(appError(400, '帳號或密碼錯誤，請重新輸入！', next));
  }
  
  res.status(200).json({
    status: 'success'
  });

}));
```
<!-- 解答：  

```javascript=
router.post('/sign_in', handleErrorAsync(async (req, res, next) => {
  const { email, password } = req.body;
  if (!email || !password) {
    next(appError(400, '帳號密碼不可為空', next));
  }
  
  const user = await User.findOne({ email }).select('+password');
  const auth = await bcrypt.compare(password, user.password);
  if (!auth) {
    next(appError(400, '帳號或密碼錯誤，請重新輸入！', next));
  }
  
  res.status(200).json({
    status: 'success'
  });

}));
```
-->

