bcryptjs密碼加密與驗證

安裝bcryptjs bcryptjs
密碼加密,此套件的加密是不可逆的,所以沒有辦法從加密後的結果回推原始密碼,相對安全性提高非常多

1
2
npm install bcryptjs --save

加密後的 bcrypt 分為四個部分:

  • Bcrypt
    該字串為 UTF-8 編碼,並且包含一個終止符
  • Round
    (回合數)每增加一次就加倍雜湊次數,預設10次
  • Salt
    (加鹽)128 bits 22個字元
  • Hash
    (雜湊)138 bits 31個字元

Node express MySql 註冊與登入

註冊

因爲註冊使用到bcryptjs密碼加密功能,
bcrypt加密語法

1
2
3
4
5
6
//引入bcryptjs密碼加密
const bcrypt = require('bcryptjs')

const saltRounds = 10;
const hashPassword = bcrypt.hashSync(密碼, saltRounds)

models.users.js

1
INSERT INTO `users` (column1, column2, column3...) SET (value1, value2, value3...)

token

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//bcryptjs密碼加密
const bcrypt = require('bcryptjs')
//載入 jsonwebtoken
const jwt = require('jsonwebtoken')
// 建立使用者
var User = function(newUser){
this.uId = newUser.uId;
this.userName = newUser.userName;
this.userEmail = newUser.userEmail;
this.userPassword = bcrypt.hashSync(newUser.userPassword, 3);//bcrypt.hashSync(newUser.userPassword, 12)
this.userPhone= newUser.userPhone;
this.roleId= newUser.roleId;
this.token = jwt.sign({_id: newUser.uId ,username: newUser.userName,email: newUser.userEmail}, 'RESTFULAPI',{expiresIn: "24h"}).toString();//jwt
this.createdAt= newUser.createdAt;
this.updatedAt= newUser.updatedAt;
this.deletedAt= newUser.deletedAt;
}
User.create = function (newUser, callback) {
db.query('INSERT INTO users SET ?', newUser, callback);
};
module.exports = User;

登入

尋找帳號與密碼驗證 => 內連結的模組資料表的資料內容 (取出modulePermissionId與modulePermissionName 並且新增到menu )
使用資料庫語法:

1
2
3
4

SELECT 顯示欄位 FROM `資料表1` INNER JOIN `資料表2` ON 資料表1uId=資料表2roleId WHERE 資料表1.欄位名 ='';
//例如
SELECT users.id, users.uId, users.userName, users.userEmail, users.userPassword, users.userPhone, users.roleId, users.token ,modulePermissions.modulePermissionId,modulePermissions.modulePermissionName FROM `users` INNER JOIN `modulePermissions` ON users.uId=modulePermissions.roleId WHERE users.userName =?;

登入時必須帳號登入時驗證密碼(bcrypt.compareSync(密碼, 密碼加密))

bcrypt驗證語法

1
bcrypt.compareSync(密碼, 密碼加密)

models.users.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
'use strict';

var db = require('../config/database');
//bcryptjs密碼加密
const bcrypt = require('bcryptjs')
//載入 jsonwebtoken
const jwt = require('jsonwebtoken')

User.userName = (userName, result) =>
{
let sql = 'SELECT users.id, users.uId, users.userName, users.userEmail, users.userPassword, users.userPhone, users.roleId, users.token ,modulePermissions.modulePermissionId,modulePermissions.modulePermissionName FROM `users` INNER JOIN `modulePermissions` ON users.uId=modulePermissions.roleId WHERE users.userName =?';
db.query(sql, userName, (err, row, fields) =>
{
if (err) result(err, null,);
result(null, row);
});
};

controllers.users.js

  • bcrypt.hashSync(密碼, SaltRound)
  • bcrypt.compareSync(密碼,加密密碼)
加密後的 bcrypt 分為四個部分:
  • Bcrypt
    該字串為 UTF-8 編碼,並且包含一個終止符
  • Round
    (回合數)每增加一次就加倍雜湊次數,預設10次
  • Salt
    (加鹽)128 bits 22個字元
  • Hash
    (雜湊)138 bits 31個字元
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
const User = require('../models/users');

exports.login = async (req, res, next) =>
{
/*
#swagger.summary='登入',
#swagger.description = '' */

/* #swagger.parameters['body'] = {
in: 'body',
description: '',
required: true,
schema: { $ref: "#/definitions/Login" }
} */

/* #swagger.security = [{
"apiKeyAuth": []
}] */

const userName = req.body.userName;
const userPassword = req.body.userPassword;

try {
//檢查是否有userName 帳號
User.userName(userName, (error, item) =>
{
if (item.length != 0 && bcrypt.compareSync(userPassword, item[0].userPassword) ) {
// console.log('登入item', item)
console.log('登入驗證密碼', bcrypt.compareSync(userPassword, item[0].userPassword))
let menu = [];
// forEach
item.forEach(el => {
let query = {
modulePermissionId: el.modulePermissionId,
modulePermissionName: el.modulePermissionName,
}
menu.push(query)
});
let loginValue = {
userName: item[0].userName,
token: item[0].token,
menu: menu,
}
return res.status(200).json(loginValue);
} else {
return res.status(400).send('沒有userName')
}
})
}
catch (error) {
return res.status(500).json({message: error.message})
}
}
routes/users.js
1
2
3
4
5
var express = require('express');
var router = express.Router();
const userController = require('../controllers/users');

router.post('/login', userController.login);

MySql 目錄

AUTO INCREMENT 欄位會自動遞增資料行的值

AUTO_INCREMENT 欄位會自動遞增資料行的值

AUTO INCREMENT 欄位會自動遞增資料行的值,因為每次新增資料時欄位值都會自動遞增也就是說 AUTO INCREMENT 欄位值會是唯一的,該欄位用途就像是一個識別碼或流水號,而 AUTO INCREMENT 常與 Primary Key 一起搭配使用。
將 users 資料表中的 id 欄位 AUTO INCREMENT 欄位會自動遞增資料行的值,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
CREATE TABLE `users` ( 
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
uId INT NOT NULL UNIQUE,
userName VARCHAR(30) NOT NULL,
userEmail VARCHAR(50) NOT NULL,
userPassword VARCHAR(30) NOT NULL,
userPhone VARCHAR(10) NOT NULL,
roleId INT NOT NULL,
createdAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updatedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deletedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE `users` (
id INT NOT NULL AUTO_INCREMENT,
uId INT NOT NULL,
userName VARCHAR(50) NOT NULL,
userEmail VARCHAR(30)NOT NULL,
userPassword VARCHAR(30) NOT NULL,
userPhone VARCHAR(10) NOT NULL,
roleId INT NOT NULL,
createdAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updatedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deletedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
PRIMARY KEY (id),
UNIQUE (uId)
);
參考:AUTO INCREMENT 欄位 (SQL AUTO INCREMENT column)

PRIMARY KEY 主鍵限制 與 AUTO INCREMENT 欄位會自動遞增資料行的值

PRIMARY KEY 主鍵限制 (SQL PRIMARY KEY Constraint)

PRIMARY KEY 用來保證欄位在資料表中的唯一性,主鍵欄位中的每一筆資料在資料表中都必需是獨一無二的。
PRIMARY KEY 有點類似 UNIQUE 加上 NOT NULL。

一個資料表中只能有一個 PRIMARY KEY,但是可以有多個 UNIQUE。
將 users 資料表中的 id 欄位設為主鍵:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
CREATE TABLE `users` ( 
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
uId INT NOT NULL UNIQUE,
userName VARCHAR(30) NOT NULL,
userEmail VARCHAR(50) NOT NULL,
userPassword VARCHAR(30) NOT NULL,
userPhone VARCHAR(10) NOT NULL,
roleId INT NOT NULL,
createdAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updatedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deletedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE `users` (
id INT NOT NULL AUTO_INCREMENT,
uId INT NOT NULL,
userName VARCHAR(50) NOT NULL,
userEmail VARCHAR(30)NOT NULL,
userPassword VARCHAR(30) NOT NULL,
userPhone VARCHAR(10) NOT NULL,
roleId INT NOT NULL,
createdAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updatedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deletedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
PRIMARY KEY (id),
UNIQUE (uId)
);

參考:PRIMARY KEY 主鍵限制 (SQL PRIMARY KEY Constraint)

AUTO_INCREMENT 欄位會自動遞增資料行的值

AUTO INCREMENT 欄位會自動遞增資料行的值,因為每次新增資料時欄位值都會自動遞增也就是說 AUTO INCREMENT 欄位值會是唯一的,該欄位用途就像是一個識別碼或流水號,而 AUTO INCREMENT 常與 Primary Key 一起搭配使用。
將 users 資料表中的 id 欄位 AUTO INCREMENT 欄位會自動遞增資料行的值,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
CREATE TABLE `users` ( 
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
uId INT NOT NULL UNIQUE,
userName VARCHAR(30) NOT NULL,
userEmail VARCHAR(50) NOT NULL,
userPassword VARCHAR(30) NOT NULL,
userPhone VARCHAR(10) NOT NULL,
roleId INT NOT NULL,
createdAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updatedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deletedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE `users` (
id INT NOT NULL AUTO_INCREMENT,
uId INT NOT NULL,
userName VARCHAR(50) NOT NULL,
userEmail VARCHAR(30)NOT NULL,
userPassword VARCHAR(30) NOT NULL,
userPhone VARCHAR(10) NOT NULL,
roleId INT NOT NULL,
createdAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updatedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deletedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
PRIMARY KEY (id),
UNIQUE (uId)
);
參考:AUTO INCREMENT 欄位 (SQL AUTO INCREMENT column)

建立一個新資料庫

SQL 語句不區分大小寫

1
CREATE DATABASE 新資料庫名稱;

參考:CREATE DATABASE 語法(Syntax)

創建table

1
2
3
4
5
6
CREATE TABLE 資料表名稱 (
欄位column_name1 欄位屬性data_type,
欄位column_name2 欄位屬性data_type,
欄位column_name3 欄位屬性data_type,
···
);
例如:建立資料表table 名稱 users
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
CREATE TABLE `users` ( 
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
uId INT NOT NULL UNIQUE,
userName VARCHAR(30) NOT NULL,
userEmail VARCHAR(50) NOT NULL,
userPassword VARCHAR(30) NOT NULL,
userPhone VARCHAR(10) NOT NULL,
roleId INT NOT NULL,
createdAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updatedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deletedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE `users` (
id INT NOT NULL AUTO_INCREMENT,
uId INT NOT NULL,
userName VARCHAR(50) NOT NULL,
userEmail VARCHAR(30)NOT NULL,
userPassword VARCHAR(30) NOT NULL,
userPhone VARCHAR(10) NOT NULL,
roleId INT NOT NULL,
createdAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updatedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deletedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
PRIMARY KEY (id),
UNIQUE (uId)
);
參考:CREATE TABLE 是我們在資料庫中用來建立一個新資料表的語法

新增資料至某資料表: INSERT INTO 是用來新增資料至某資料表 (table)

1
2
3
4
5
INSERT INTO table_name (column1, column2, column3...)
VALUES (value1, value2, value3...);
or
INSERT INTO table_name
VALUES (value1, value2, value3...);
例如:新增資料至users資料表
1
INSERT INTO `users`(`uId`, `userName`, `userEmail`, `userPassword`, `userPhone`, `roleId`, `createdAt`, `updatedAt`, `deletedAt`) VALUES ('1735223063','laraHuang','laraHuang@gmail.com','Lara123456','0910123456','1735223063','2024-12-26 22:24:23','2024-12-26 22:24:23','2024-12-26 22:24:23')

參考:INSERT INTO 是用來新增資料至某資料表 (table)

TypeScript type 與interface 區別

Type

1
2
3
4
5
type Point = {
x: number;
y: number;
};
type SetPoint = (x: number, y: number) => void;

Interface

1
2
3
4
5
6
7
interface Point {
x: number;
y: number;
}
interface SetPoint {
(x: number, y: number): void;
}

Interface都可允许拓展(extends)

Interface extends Type
1
2
3
4
type Person{
name:string
}
interface Student extends Person { stuNo: number }
Interface extends Interface
1
2
3
4
interface Person{
name:string
}
interface Student extends Person { stuNo: number }
type extends type
1
2
3
4
type Person{
name:string
}
type Student = Person & { stuNo: number }
type extends Interface
1
2
3
4
interface Person{
name:string
}
type Student = Person & { stuNo: number }
實現 implements
類別可以實作interface以及type(除聯合類型外)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface ICat{
setName(name:string): void;
}
class Cat implements ICat{
setName(name:string):void{
// todo
}
}
// type
type ICat = {
setName(name:string): void;
}
class Cat implements ICat{
setName(name:string):void{
// todo
}
}
type 無法對聯合類型Person進行實現
無法對聯合類型Person進行實現
error: A class can only implement an object type or intersection of object types with statically known members.
1
2
3
4
5
6
7
8
9
type Person = { name: string; } | { setName(name:string): void };
// 無法對聯合類型Person進行實現
// error: A class can only implement an object type or intersection of object types with statically known members.
class Student implements Person {
name= "Lara";
setName(name:string):void{
// todo
}
}

type 可以宣告基本型別別名,聯合型別類型,元組等型別,而 interface 不行

type可以宣告基本型別別名,聯合型別類型,元組等型別

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 基本類型别名
type Name = string

// 聯合類型
interface Dog {
wong();
}
interface Cat {
miao();
}

type Pet = Dog | Cat

// 具体定義數組每個位置的類型
type PetList = [Dog, Pet]

type 語句中還可以使用 typeof 獲取實例的 類型進行赋值

1
2
3
// 當你想獲取一個變量的類型時,使用 typeof
let div = document.createElement('div');
type B = typeof div
1
2
3
4
5
6
7
type StringOrNumber = string | number;  
type Text = string | { text: string };
type NameLookup = Dictionary<string, Person>;
type Callback<T> = (data: T) => void;
type Pair<T> = [T, T];
type Coordinates = Pair<number>;
type Tree<T> = T | { left: Tree<T>, right: Tree<T> };

interface 可以聲明合併而 type 不行

interface 能够聲明合併

1
2
3
4
5
6
7
8
interface User {
name: string
age: number
}

interface User {
sex: string
}

事件循環 (Event Loop)

JavaScript :是單執行緒的程式語言,一行程式碼執行完才會再執行下一行,這個概念稱之為同步 (synchronous)。

使用者體驗,於是就有了異步 (asynchronous) 的概念
透過事件循環機制,能有效解決 JavaScript 單執行緒的問題,讓耗時的操作不會阻塞主線成。

  • 堆 (Heap):堆是一種數據結構,拿來儲存物件
  • 棧 (Stack):採用後進先出的規則,當函式執行時,會被添加到棧的頂部,當執行完成時,就會從頂部移出,直到棧被清空
  • 隊列 (Queue):也是一種數據結構,特性是先進先出 (FIFO)。在 JavaScript 的執行環境中,等待處理的任務會被放在隊列(Queue) 裡面,等待棧 (Stack) 被清空時,會從隊列(Queue)中拿取第一個任務進行處理
事件循環(Event loop):事件循環會不斷地去查看棧(Stack) 是否空出,如果空出就會把隊列(Queue)中等待的任務放進棧(Stack)中執行.
步驟
所有任務都會在主線程上執行,形成一個執行棧 =>
如果遇到異步任務,例如:setTimeout,執行環境會調用相關的 API (例如在瀏覽器上會調用 Web API),等待此異步任務的結果之後,再被放置到任務隊列中 =>
一旦執行棧的所有同步任務完成之後,就會讀取任務隊列,並將任務隊列第一個,加到執行棧中運行 =>
只要執行棧空了之後,就會讀取任務隊列,不斷重複這個步驟,直到所有任務完成,這個流程就是**事件循環 (Event loop) **

CSS object-fit

CSS object-fit屬性的用途是什麼?

Ans: 調整影像或影片的大小以適合其容器。

CSS object-fit 屬性指定元素的內容應該如何去適應指定容器的高度與寬度。

object-fit 是一個 CSS 屬性,用於決定一個可替換 resource 的 element(img, video, iframe…)如何適應定義好寬高的 HTML 標籤。

常用的屬性值

object-fit: cover

保持其縱橫比並填充給定的尺寸。圖像將被剪裁以適合

object-fit: contain

圖像保持其縱橫比,但調整大小以適合給定的尺寸

object-fit: fill;

會調整其大小以填充給定的尺寸。如果有必要,圖像將被拉伸或擠壓以適應

object-fit: none;

未調整大小的圖像

object-fit: scale-down

縮小到最小版本的圖像

參考資料