## interior_helper 開發-後端學習筆記
bcrypt 處理密碼
---
```javascript
bcrypt.genSalt(10) // 產生「鹽」,並設定複雜度係數為 10
.then(salt => bcrypt.hash(password, salt)) // 為使用者密碼「加鹽」,產生雜湊值
```
```javascript
bcrypt.compare(password, user.password)
```
JWT 認證機制
---
cookie-based機制 / token-based機制

`cookie & session 的機制`
* cookie:用戶端的憑證,有如會員卡,卡上有會員編號 (也就是 session_id)
* session:伺服器端的憑證對照表,有如會員名冊,可以透過會員編號 (session_id) 查找到使用者資訊 (user_id)
cookie 是瀏覽器存放身份憑證的儲存機制, session 是伺服器存放身份憑證的儲存機制。

用戶端、應用程式伺服器、資料庫三個地方的資訊匹配起來

*有個問題點:cookie 的值只能在特定網域內被存取 沒辦法跨網域使用*
---
`JWT Token`
由三部分組成:header.payload.signature
* Header - 標記 token 的類型與雜湊函式名稱
* Payload - 要攜帶的資料,例如 user_id 與時間戳記,也可以指定 token 的過期時間
* Signature - 根據 Header 和 Payload,加上密鑰 (secret) 進行雜湊,產生一組不可反解的亂數,當成簽章,用來驗證 JWT 是否經過篡改
```javascript
Header(標頭):
{
"alg": "HS256", // 使用的加密算法
"typ": "JWT" // token 類型
}
Payload(負載):
{
"id": "user_id", // 自定義數據
}
Signature(簽名):
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
JWT_SECRET
)
```
---
res.cookie() 是 Express.js 中用於設置 HTTP Cookie 的方法
```javascript
{
// 禁止客戶端 JavaScript 訪問此 Cookie
httpOnly: true,
// 只在 HTTPS 連接中傳輸
secure: process.env.NODE_ENV === 'production',
// Cookie 的有效期(毫秒)
maxAge: 30 * 24 * 60 * 60 * 1000, // 30天
// 可選:Cookie 的路徑
path: '/',
// 可選:Cookie 的域名
domain: 'your-domain.com',
// 可選:防止 CSRF 攻擊
sameSite: 'strict',
}
```
2. 身份驗證middleware:
用於驗證 token 的有效性和解碼 token 的內容
```javascript=
const decoded = jwt.verify(token, process.env.JWT_SECRET!) as JwtCustomPayload;
```
區別
| JWT_SECRET | Token | |
| ----- |:------------------------------------------ | -------- |
|是用於加密/解密的密鑰 | 是加密後的結果
保存在服務器端 | 發送給客戶端保存
永遠不會發送給客戶端 | 客戶端每次請求都會發送
通常較短且簡單 | 可以被解碼查看內容(但無法偽造,因為沒有密鑰) |
```javascript=
// 1. JWT_SECRET(在 .env 文件中)
JWT_SECRET=myVerySecretKey123
// 2. 創建 token(在 login 函數中)
const token = jwt.sign(
{
id: "123",
email: "user@example.com",
role: "user"
},
process.env.JWT_SECRET!, // 使用密鑰進行簽署
{ expiresIn: '7d' }
);
// token 會是類似這樣的:
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEyMyIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsInJvbGUiOiJ1c2VyIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
// 3. 驗證 token
const decoded = jwt.verify(token, process.env.JWT_SECRET!) as JwtCustomPayload;
// decoded 會是:
// {
// id: "123",
// email: "user@example.com",
// role: "user",
// iat: 1516239022,
// exp: 1516843822
// }
```
在前端讀取 Cookie:
```javascript
// 如果 Cookie 不是 httpOnly
document.cookie // 可以讀取非 httpOnly 的 Cookie
// 如果是 httpOnly Cookie,只能通過後端 API 訪問
fetch('/api/auth/user', {
credentials: 'include' // 必須設置才能發送 Cookie
});
```
Authorization
---
let token = req.headers('authorization')
```javascript=
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer')) {
token = req.headers.authorization.split(' ')[1];
} else if (req.cookies?.token) {
token = req.cookies.token;
}
if (!token) {
res.status(401).json({ message: 'Acess Denied' });
return;
}
const verified = jwt.verify(token, process.env.JWT_SECRET as string) as { id: string };
```
auth.config.ts 與 auth.ts
---
```javascript=
//auth.ts
export const {
auth, handlers: { GET, POST }, signIn, signOut,
} = NextAuth({
adapter: PrismaAdapter(prisma),
session: { strategy: 'jwt' },
...authConfig,
})
// auth.config.ts
export default {
providers: [ // 定義認證提供者
Credentials({ // 使用憑證(用戶名密碼)方式認證
```
auth.ts:認證的主要實現檔案
整合了各種認證相關的功能:
* 使用 PrismaAdapter 來處理資料庫操作
* 設定 session 策略為 JWT
* 導入 auth.config.ts 的配置(通過 ...authConfig)
---
不需要安裝 body-parser,原因如下
```javascript=
// 這些行已經在你的 index.ts 中
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
```
> 生成 Cloudinary 上傳所需的簽名
簽名的生成過程在服務器端完成,這樣可以保護 API 密鑰的安全性
```javascript=
export async function POST(request: Request) {
const body = (await request.json()) as {paramsToSign: Record<string, string>}
const {paramsToSign} = body;
const signature = cloudinary.v2.utils.api_sign_request(paramsToSign,
process.env.CLOUDINARY_API_SECRET as string);
return Response.json({signature});
}
```
---
const isLoggedIn = !!req.auth 可以寫成 const isLoggedIn = req.auth 嗎?
```javascript=
export default auth((req) =>{
const isLoggedIn = !!req.auth;
```
雙重否定 (!!)
無論 req.auth 是什麼類型(例如 undefined、null、空字串、物件等),!! 確保 isLoggedIn 是布林值。
```javascript=
console.log(!!null); // false
console.log(!!undefined); // false
console.log(!!{}); // true
console.log(!!''); // false
```
```javascript
```