Xin chào các bạn, sau một vài bài lý thuyết về NodeJS thì hôm nay chúng ta cùng đi vào thực hành nhé. Và hôm nay chúng ta sẽ làm bước một bước mình nghĩ là khá quan trọng trong quá trình xây dựng API, đó chính là Authenticate và Authorization. Mình sẽ không nói nhiều về 2 khái niệm này và tại sao chúng ta phải làm, cũng đã có rất nhiều bài viết so sánh hai khái niệm này.
Trong bài viết này thì mình sẽ sử dụng:
- NodeJS
- Express
- MongoDB
Tạo project
Chúng ta sẽ sử dụng express generator để tự động tạo app. Bạn có thể vào trang sau để rõ hơn: https://expressjs.com/en/starter/generator.html
Chạy lệnh sau để cài đặt express-generator
1 2 | npm install -g express-generator |
Sau đó chạy
1 2 | express |
để generate code
Bạn sẽ thấy có 1 vài folder bạn không có vì do mình tự tạo thêm thôi, cái này mình sẽ hướng dẫn ở phần sau
Ở thư mục dự án, hãy chạy
1 2 | npm install |
và chạy
1 2 | npm start |
để khởi tạo app. App sẽ được chạy mặc định ở port 3000. Bạn có thể tạo một file .env
để thay đổi port hoặc thay đổi port mặc định ở trong binwwww
. Nhưng có vấn đề là chả lẽ mỗi lần sửa đổi file gì sẽ phải start lại 1 lần thì rất mất công. Vậy hãy cài thêm nodemon
để action này được tự động nhé. Chi tiết cách cài đặt bạn có thể xem tại đây: https://www.npmjs.com/package/nodemon.
Sau khi đã cài thành công nodemon
bạn vào file package.json
, thay đổi start
trong script
thành nodemon ./bin/www
1 2 3 4 | <span class="token string">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string">"start"</span><span class="token operator">:</span> <span class="token string">"nodemon ./bin/www"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> |
Và giờ hãy start lại npm, sau đó thử thay đổi 1 file nào đó nhé.
Tạo DB
Tại sao lại là mongoDB? Thật ra là do mình chưa làm việc với mongoDB, nên muốn tìm hiểu nó là chính, đơn giản vậy thôi. Các bạn hãy thuy cập vào trang web https://www.mongodb.com/, sau đó sign in vào. Sau khi đồng ý với điều khoản và chọn gói free (nếu bạn có muney). Sau đó hãy tới những thiết lập cho sv, phần này thì bạn có thể tùy chọn bên cùng cấp, vị trí đặt server,… Sau đó ấn Create Cluster
Sau đó thì sẽ xuất hiện 1 bảng nhỏ có những bước sau, hãy hoàn thành đủ các bước nhé, khi ấn vào từng bước sẽ có hướng dẫn cụ thể nên mình sẽ không nói nữa nha
Chi tiết về từng bước như sau, ngoại trừ bước đầu đã gạch nha:
- Tạo người dùng cho db (ở đây bạn sẽ được tạo người dùng và phân quyền cho người dùng đó)
- Thiết lập whitelist cho địa chị IP (quy định những ip nào có thể truy cập, bạn cũng có thể cho phép truy cập từ toàn bộ các ip)
- Load data mẫu (cái này làm hay không cũng được, nhưng mình thích nhìn nó lên 100% nên sẽ làm)
- Kết nối tới cluster của bạn (riêng cái này mình sẽ hướng dẫn chi tiết ở dưới)
Ở đây sẽ có 3 options cho bạn chọn
- Kết nối với mongo shell
- Kết nối với app của bạn
- Kết nối sử dụng MongoDB Compass
Ở đây mình sẽ sử dụng số 2 và 3, số 2 để mình sử dụng trong project để kết nối, còn số 3 thì như là giao diện để mình xem (tương tự viêc bạn sử dụng phpmyadmin, mysql workbench,…). Trước tiên mình sẽ tải mongoDB compass. Bạn hãy click vào option thứ 3 và tải về rồi cài đặt nhé
Sau khi cài đặt xong, bạn hãy copy đoạn ở phần 2, sau đó thay password
bằng password của user bạn tạo rồi ấn connect trong app MongoDB Compass
Và đây là kết quả. Do đã load sample data nên mình có khá nhiều db, và mình có tạo thêm db là express để demo
Vậy là chúng ta đã tạo xong DB
Kết nối DB
Giờ để kết nối và thao tác với DB, chúng ta sẽ sử dụng mongoose
https://www.npmjs.com/package/mongoose.
Đầu tiền hãy tạo file .env
để lưu những thiết lập này, chứ không thể lưu trực tiếp trong code được, vì như vậy nếu có lỡ để public repo ra thì không khác gì bạn đang cho tất cả mọi người kết nối vào db của mình. Bạn hãy vào lại trang web của mongodb. Bạn hãy click vào option thứ 2 ở phần mình hướng dẫn phía trên. Còn nếu đã tắt đi rồi thì có thể ấn vào nút connect
.
Giờ hãy copy giá trị của const uri
thôi nhé và paste vào .env
nhớ điền password
và dbname
.env
1 2 | DB_CONNECT=mongodb+srv:..... |
Ở trong file app.js
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">const</span> mongoose <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'mongoose'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> dotenv <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'dotenv'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> dotenv<span class="token punctuation">.</span><span class="token function">config</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> mongoose<span class="token punctuation">.</span><span class="token function">connect</span><span class="token punctuation">(</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">DB_CONNECT</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> useUnifiedTopology<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> useNewUrlParser<span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'DB Connected'</span><span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> |
Giờ hãy để ý trong terminal, chỗ mà đang chạy sv, nếu thấy dòng DB Connected
tức là đã kết nối thành công rồi đó
Authenticate
Trước khi authenticate thì chúng ta sẽ phải có user. Vì vậy mình sẽ tạo 1 api để thêm user trước. Đầu tiên hãy tạo model nhé
models/User.js
:
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 | <span class="token keyword">const</span> mongoose <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'mongoose'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> userSchema <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">mongoose<span class="token punctuation">.</span>Schema</span><span class="token punctuation">(</span><span class="token punctuation">{</span> name<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> String<span class="token punctuation">,</span> required<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> min<span class="token operator">:</span> <span class="token number">6</span><span class="token punctuation">,</span> max<span class="token operator">:</span> <span class="token number">255</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> email<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> String<span class="token punctuation">,</span> required<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> min<span class="token operator">:</span> <span class="token number">6</span><span class="token punctuation">,</span> max<span class="token operator">:</span> <span class="token number">225</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> password<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> String<span class="token punctuation">,</span> required<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> min<span class="token operator">:</span> <span class="token number">6</span><span class="token punctuation">,</span> max<span class="token operator">:</span> <span class="token number">255</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> module<span class="token punctuation">.</span>exports <span class="token operator">=</span> mongoose<span class="token punctuation">.</span><span class="token function">model</span><span class="token punctuation">(</span><span class="token string">'User'</span><span class="token punctuation">,</span> userSchema<span class="token punctuation">)</span><span class="token punctuation">;</span> |
Chúng ta khởi tạo 1 schema và đặt điều kiện cho từng key trong nó.
Vì có điều kiện nên khi có request gửi lên chắc chắn ta sẽ phải validate, mongoose cũng có hỗ trợ validate nhưng mình sẽ không dùng mà sẽ dùng 1 package là Joi
, Bạn có thể tự tìm hiểu thêm về package này nhé, mình sẽ không đi vào chi tiết
validations/auth.js
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token keyword">const</span> Joi <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'joi'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">registerValidator</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> rule <span class="token operator">=</span> Joi<span class="token punctuation">.</span><span class="token function">object</span><span class="token punctuation">(</span><span class="token punctuation">{</span> name<span class="token operator">:</span> Joi<span class="token punctuation">.</span><span class="token function">string</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">min</span><span class="token punctuation">(</span><span class="token number">6</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">max</span><span class="token punctuation">(</span><span class="token number">225</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">required</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> email<span class="token operator">:</span> Joi<span class="token punctuation">.</span><span class="token function">string</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">min</span><span class="token punctuation">(</span><span class="token number">6</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">max</span><span class="token punctuation">(</span><span class="token number">225</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">required</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">email</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> password<span class="token operator">:</span> Joi<span class="token punctuation">.</span><span class="token function">string</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">pattern</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">RegExp</span><span class="token punctuation">(</span><span class="token string">'^[a-zA-Z0-9]{6,20}$'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">required</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token keyword">return</span> rule<span class="token punctuation">.</span><span class="token function">validate</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> module<span class="token punctuation">.</span>exports<span class="token punctuation">.</span>registerValidator <span class="token operator">=</span> registerValidator<span class="token punctuation">;</span> |
Tạo thêm file routes/auth.js
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 | <span class="token keyword">const</span> express <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'express'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> jwt <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'jsonwebtoken'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> bcrypt <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'bcryptjs'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> router <span class="token operator">=</span> express<span class="token punctuation">.</span><span class="token function">Router</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> User <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./../models/User'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token punctuation">{</span> registerValidator <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./../validations/auth'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> router<span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">'/register'</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token parameter">request<span class="token punctuation">,</span> response</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token punctuation">{</span> error <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">registerValidator</span><span class="token punctuation">(</span>request<span class="token punctuation">.</span>body<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token keyword">return</span> response<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">422</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span>error<span class="token punctuation">.</span>details<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>message<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> checkEmailExist <span class="token operator">=</span> <span class="token keyword">await</span> User<span class="token punctuation">.</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token punctuation">{</span> email<span class="token operator">:</span> request<span class="token punctuation">.</span>body<span class="token punctuation">.</span>email <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>checkEmailExist<span class="token punctuation">)</span> <span class="token keyword">return</span> response<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">422</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token string">'Email is exist'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> salt <span class="token operator">=</span> <span class="token keyword">await</span> bcrypt<span class="token punctuation">.</span><span class="token function">genSalt</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> hashPassword <span class="token operator">=</span> <span class="token keyword">await</span> bcrypt<span class="token punctuation">.</span><span class="token function">hash</span><span class="token punctuation">(</span>request<span class="token punctuation">.</span>body<span class="token punctuation">.</span>password<span class="token punctuation">,</span> salt<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> user <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">User</span><span class="token punctuation">(</span><span class="token punctuation">{</span> name<span class="token operator">:</span> request<span class="token punctuation">.</span>body<span class="token punctuation">.</span>name<span class="token punctuation">,</span> email<span class="token operator">:</span> request<span class="token punctuation">.</span>body<span class="token punctuation">.</span>email<span class="token punctuation">,</span> password<span class="token operator">:</span> hashPassword<span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> newUser <span class="token operator">=</span> <span class="token keyword">await</span> user<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">await</span> response<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span>newUser<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span> response<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">400</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> module<span class="token punctuation">.</span>exports <span class="token operator">=</span> router<span class="token punctuation">;</span> |
Và để sử dụng được thì trong app.js
sẽ thêm đoạn sau:
1 2 | app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token string">'/api/auth'</span><span class="token punctuation">,</span> authRouter<span class="token punctuation">)</span><span class="token punctuation">;</span> |
Giải thích chút thì ban đầu nếu có error thì mình sẽ trả về 402 và message của nó. Bạn có thể console.log error ra là sẽ rõ, hoặc có thể log cả đoạn registerValidator(request.body)
để thấy được tất cả những gì trả về. Để có thể sử dụng trong postman thì bạn dùng
1 2 | return response.send(registerValidator(request.body)); |
để dễ nhìn hơn nhé. Giờ hãy thử mở postman lên và tạo thử tài khoản những không khớp với validation rule xem sao
Như bạn thấy email đã bị báo sai định dạng rồi nè. Quay trở về code nào, sau đó thì bạn có thấy mình sẽ check xem email có tồn tại không, điều này là dĩ nhiên vì mình không muốn tồn tại 2 email giống nhau trong hệ thống của mình được. Tiếp tới thì mình có mã hóa mật khẩu, lib mình sử dung là bcryptjs
nhé. Cuối cùng thì save lại user và send thông tin đó lên thôi.
Vậy là chúng ta đã tạo thành công user rồi
Sau khi có tài khoản rồi thì chúng ta phải đăng nhập vào hệ thống chứ nhỉ
Quay lại file routes/auth.js
1 2 3 4 5 6 7 8 9 10 11 | router<span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">'/login'</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token parameter">request<span class="token punctuation">,</span> response</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> user <span class="token operator">=</span> <span class="token keyword">await</span> User<span class="token punctuation">.</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token punctuation">{</span>email<span class="token operator">:</span> request<span class="token punctuation">.</span>body<span class="token punctuation">.</span>email<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>user<span class="token punctuation">)</span> <span class="token keyword">return</span> response<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">422</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token string">'Email or Password is not correct'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> checkPassword <span class="token operator">=</span> <span class="token keyword">await</span> bcrypt<span class="token punctuation">.</span><span class="token function">compare</span><span class="token punctuation">(</span>request<span class="token punctuation">.</span>body<span class="token punctuation">.</span>password<span class="token punctuation">,</span> user<span class="token punctuation">.</span>password<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>checkPassword<span class="token punctuation">)</span> <span class="token keyword">return</span> response<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">422</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token string">'Email or Password is not correct'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> response<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">User </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>user<span class="token punctuation">.</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> has logged in</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> |
Giờ hãy tạo thêm 1 api lấy danh sách users nhỉ
routes.users
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token keyword">const</span> express <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'express'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> User <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./../models/User'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> router <span class="token operator">=</span> express<span class="token punctuation">.</span><span class="token function">Router</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> router<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'/'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">request<span class="token punctuation">,</span> response</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> User<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">exec</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">err<span class="token punctuation">,</span> users</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> response<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span>users<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> module<span class="token punctuation">.</span>exports <span class="token operator">=</span> router<span class="token punctuation">;</span> |
app.js
1 2 | app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token string">'/api/users'</span><span class="token punctuation">,</span> userRouter<span class="token punctuation">)</span><span class="token punctuation">;</span> |
Và đây là danh sách users của chúng ta
Từ đã, tại sao lại có thể sử dụng api 1 cách dễ dàng vậy nhỉ, thế này thì sai quá. Và đó là lí do chúng ta sẽ đến tới với phần tiếp theo
Authorization
Về cơ bản chúng ta sẽ tạo ra một token xác thực, mỗi request người dùng gửi lên đều phải đi kèm với token đó, nếu đúng token thì người dùng mới có thể gọi được tới những api mà chúng ta yêu cầu phải xác thực. Và chúng ta sẽ sử dụng JWT
https://www.npmjs.com/package/jsonwebtoken.
Đầu tiện để tạo ra token, thì mỗi khi người dùng đăng nhập chúng ta sẽ tạo ra một token cho người dùng đó. Giờ chúng ta sẽ phải sửa lại chức năng đăng nhập một chút
.env
1 2 | TOKEN_SECRET=somethingrandom |
routes/auth.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token keyword">const</span> jwt <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'jsonwebtoken'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> router<span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">'/login'</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token parameter">request<span class="token punctuation">,</span> response</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> user <span class="token operator">=</span> <span class="token keyword">await</span> User<span class="token punctuation">.</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token punctuation">{</span>email<span class="token operator">:</span> request<span class="token punctuation">.</span>body<span class="token punctuation">.</span>email<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>user<span class="token punctuation">)</span> <span class="token keyword">return</span> response<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">422</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token string">'Email or Password is not correct'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> checkPassword <span class="token operator">=</span> <span class="token keyword">await</span> bcrypt<span class="token punctuation">.</span><span class="token function">compare</span><span class="token punctuation">(</span>request<span class="token punctuation">.</span>body<span class="token punctuation">.</span>password<span class="token punctuation">,</span> user<span class="token punctuation">.</span>password<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>checkPassword<span class="token punctuation">)</span> <span class="token keyword">return</span> response<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">422</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token string">'Email or Password is not correct'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> token <span class="token operator">=</span> jwt<span class="token punctuation">.</span><span class="token function">sign</span><span class="token punctuation">(</span><span class="token punctuation">{</span>_id<span class="token operator">:</span> user<span class="token punctuation">.</span>_id<span class="token punctuation">}</span><span class="token punctuation">,</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">TOKEN_SECRET</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> expiresIn<span class="token operator">:</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">24</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> response<span class="token punctuation">.</span><span class="token function">header</span><span class="token punctuation">(</span><span class="token string">'auth-token'</span><span class="token punctuation">,</span> token<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span>token<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> |
Bạn sẽ thấy sau khi validate xong mình sẽ tạo ra 1 token dựa trên id của user, sau đó sẽ có sử dụng token_secret
, token_secret
vô cùng quan trọng vì nếu thông tin user lộ ra thì token cũng chưa thể bị đánh cắp ngay vì còn token_secret
. Bạn có thể tạo 1 chuỗi string bất kì, ở đây thì mình ghi tạm là somethingrandom
thôi chứ mình thậm chí còn gõ 1 chuỗi linh tinh r mã hóa nó lại cơ :v. Và option cuối cùng là những tùy chỉnh bạn muốn thêm, ở đây mình sẽ set thời gian hết hạn cho token là 1 ngày.
Tiếp tới hãy tạo thêm middleware để xác thực api nhé
middlewares/verifyToken.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <span class="token keyword">const</span> jwt <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'jsonwebtoken'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">request<span class="token punctuation">,</span> response<span class="token punctuation">,</span> next</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> token <span class="token operator">=</span> request<span class="token punctuation">.</span><span class="token function">header</span><span class="token punctuation">(</span><span class="token string">'auth-token'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>token<span class="token punctuation">)</span> <span class="token keyword">return</span> response<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">401</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token string">'Access Denied'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> verified <span class="token operator">=</span> jwt<span class="token punctuation">.</span><span class="token function">verify</span><span class="token punctuation">(</span>token<span class="token punctuation">,</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">TOKEN_SECRET</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> response<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">400</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token string">'Invalid Token'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> |
Ở đây mình sẽ kiểm tra trong auth-token
ở trong header (cái này thì bạn đặt là gì cũng được nhé, đừng đặt trùng với nhưng cái mặc định là được). Nếu không có token thì sẽ trả về 401, còn nếu có nhưng mà token không đúng với token được gen ra thì sẽ là 400.
Giờ ở trong routes.users
sẽ bổ sung middleware:
1 2 3 4 5 6 7 8 | <span class="token keyword">const</span> verifyToken <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./../middlewares/verifyToken'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> router<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'/'</span><span class="token punctuation">,</span> verifyToken<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">request<span class="token punctuation">,</span> response</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> User<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">exec</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">err<span class="token punctuation">,</span> users</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> response<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span>users<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Giờ hãy thử truy cập lại nhé
Giờ thì không được đâu sói ạ. Muốn có thể dùng api này thì bạn hay đăng nhập lại nhé. Giờ sau khi đăng nhập lại thì sẽ có 1 token cho bạn, hay copy token đó và đưa vào header. Key: auth-token, value sẽ là token bạn copy đó
Vậy là đã xong, mong bài viết này sẽ giúp íc được các bạn phần nào
Đây là repo của mình nếu bạn nào muốn xem lại code nhé:
https://github.com/duongmanhhoang/demo-node-js