---
# 9.2帳密登陸管理+存檔案
---
# 9.2帳密登陸管理+存檔案
## 將密碼加密後=>儲存/供登陸比對
## 註冊
routes
```javascript=
import express from 'express'
import { register } from '../controller/user.js'
const router = express.Router()
router.post('/', register)
export default router
```
controller
```javascript=
import users from '../models/users.js'
import bcrypt from 'bcrypt'
export const register = async (req, res) => {
try {
// 略過驗證密碼長度/帳號重複等
req.body.password = bcrypt.hashSync(req.body.password, 10)
await users.create(req.body)
res.status(200).json({ success: true, message: '' })
}
```
---
## 登陸
routes
```javascript=
// 之前內容略過
import { login } from '../controller/user.js'
import * as auth from '../middleware/auth.js'
router.post('/login', auth.login, login)
```
passport
```javascript=
// 之前內容略過
import passport from 'passport'
import bcrypt from 'bcrypt'
import passportLocal from 'passport-local'
import passportJWT from 'passport-jwt'
import users from '../models/users.js'
const LocalStrategy = passportLocal.Strategy
// bcrypt驗證帳秘(供middleware匯入用)
passport.use('login', new LocalStrategy({
usernameField: 'username',
passwordField: 'password'
},
async (username, password, done) => {
try {
const user = await users.findOne({ username })
if (!user) {
return done(null, false, { message: '無該帳號' })
}
if (!bcrypt.compareSync(password, user.password)) {
return done(null, false, { message: '密碼錯誤' })
}
return done(null, user)
} catch (err) {
return done(err, false)
}
}
))
```
middleware
```javascript=
// 無使用到的略過
import passport from 'passport'
import '../passport/passport.js'
// passport 驗證過後,才req.user = user 並下一步,不然報錯
export const login = (req, res, next) => {
passport.authenticate('login', { session: false }, (err, user, info) => {
if (err) {
res.status(400).json({ success: false, message: err })
return
}
if (!user) {
res.status(400).json({ success: false, message: info.message })
return
}
req.user = user
next()
})(req, res, next)
}
```
controller
.env 新增
JWT_SECRET=隨便打一段密鑰
```javascript=
// 無使用到的略過
import bcrypt from 'bcrypt'
import jwt from 'jsonwebtoken'
export const login = async (req, res) => {
// 把id變成不易讀token (內容,加密鑰,過期時間)
const token = jwt.sign({ _id: req.user._id.toString() }, process.env.JWT_SECRET, { expiresIn: '1h' })
req.user.tokens.push(token)
await req.user.save()
res.status(200).json({ success: true, message: '登陸成功', token })
}
```
---
## 讀取
routes
```javascript=
// 之前內容略過
import { getData } from '../controller/user.js'
import * as auth from '../middleware/auth.js'
router.get('/me', auth.jwt, getData)
```
passport
```javascript=
// 之前內容略過
import passport from 'passport'
import passportJWT from 'passport-jwt'
import users from '../models/users.js'
const JWTStrategy = passportJWT.Strategy
const ExtractJWT = passportJWT.ExtractJwt
// passport 使用JWTStrategy
// 讀取Bearer的token+ 使用JWT_SECRET解碼 +紀錄Req供使用
//
passport.use('jwt', new JWTStrategy({
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
secretOrKey: process.env.JWT_SECRET,
passReqToCallback: true
},
async (req, payload, done) => {
// 拆解出token 跟使用者檢查是否相符/未過期
const token = req.headers.authorization.split(' ')[1]
try {
const user = await users.findOne({ _id: payload._id, tokens: token })
if (user) {
return done(null, { user, token })
}
return done(null, false, { message: '請重新登錄' })
} catch (err) {
return done(err, false)
}
}
))
```
middleware
```javascript=
// 無使用到的略過
import passport from 'passport'
import jsonwebtoken from 'jsonwebtoken'
import '../passport/passport.js'
export const jwt = (req, res, next) => {
passport.authenticate('jwt', { session: false }, (err, data, info) => {
if (err) {
res.status(400).json({ success: false, message: err })
return
}
if (!data) {
if (info instanceof jsonwebtoken.JsonWebTokenError) {
res.status(400).json({ success: false, message: 'JWT錯誤' })
} else {
res.status(400).json({ success: false, message: info.message })
}
return
}
req.user = data.user
req.token = data.token
next()
})(req, res, next)
}
```
controller
```javascript=
// 無使用到的略過
export const getData = async (req, res) => {
res.status(200).json({
success: true,
message: '',
result: {
_id: req.user._id,
username: req.user.username,
avatar: req.user.avatar
}
})
}
```
---
## 登出
routes
```javascript=
// 之前內容略過
import { logout } from '../controller/user.js'
router.delete('/logout', auth.jwt, logout)
```
controller
```javascript=
// 無使用到的略過
export const logout = async (req, res) => {
try {
req.user.tokens = req.user.tokens.filter(tk => tk !== req.token)
await req.user.save()
res.status(200).json({ success: true, message: '登出成功' })
} catch (err) {
res.status(500).json({ success: false, message: '伺服器錯誤' })
}
}
```
---
# 新增頭貼
## 辦帳號
雲端存檔案平台=>註冊
記得要改成下方的選單 不然要重辦帳號

npm i bcrypt(加密.?) passport(驗證登錄套件 ex:discord line 等) passport-jwt passport-local



---
## 上傳檔案
npm i multer(處理上傳檔案) cloudinary() multer-storage-cloudinary(處理後再傳到雲端)
routes
```javascript=
// 之前內容略過
import { editAvatar } from '../controller/user.js'
import upload from '../middleware/upload.js'
router.patch('/avatar', auth.jwt, upload, editAvatar)
```
middleware => upload.js
.env 新增
CLOUDINARY_NAME=
CLOUDINARY_KEY=
CLOUDINARY_SECRET=
```javascript=
// 無使用到的略過
import multer from 'multer'
import { v2 as cloudinary } from 'cloudinary'
import { CloudinaryStorage } from 'multer-storage-cloudinary'
// 帳密供登陸cloudinary
cloudinary.config({
cloud_name: process.env.CLOUDINARY_NAME,
api_key: process.env.CLOUDINARY_KEY,
api_secret: process.env.CLOUDINARY_SECRET
})
// 定義 上傳方式/克制限制/基本限制
const upload = multer({
storage: new CloudinaryStorage({ cloudinary }),
fileFilter (req, file, callback) {
if (!file.mimetype.includes('image')) {
callback(new multer.MulterError('LIMIT_FORMAT'), false)
} else {
callback(null, true)
}
},
limits: {
fileSize: 1024 * 1024
}
})
// 如果有err => 是multer的就是上船相關(自定義文字)/伺服器=>正常上傳下一步
export default async (req, res, next) => {
upload.single('image')(req, res, err => {
if (err instanceof multer.MulterError) {
let message = '上傳錯誤'
if (err.code === 'LIMIT_FORMAT') {
message = '檔案格式錯誤'
} else if (err.code === 'LIMIT_FILE_SIZE') {
message = '檔案太大'
}
res.status(400).json({ success: false, message })
} else if (err) {
res.status(500).json({ success: false, message: '伺服器錯誤' })
} else {
next()
}
})
}
```
controller
```javascript=
// 無使用到的略過
export const editAvatar = async (req, res) => {
try {
// 把上船的檔案路徑,加到該用戶內
req.user.avatar = req.file.path
await req.user.save()
res.status(200).json({ success: true, message: '上傳成功' })
} catch (err) {
res.status(500).json({ success: false, message: '伺服器錯誤' })
}
}
```
讀FORM-DATA 靠下方 MULTER

