Cách chức năng đăng nhập hoạt động — Xác thực, session và Cookie

Bài viết này nằm trong khóa Kiến thức nền tảng về CNTT, xây dựng từ đầu những kiến thức CNTT thực tế tối thiểu mà bạn cần để lập trình và vibe coding.
Chức năng đăng nhập gồm xác thực để xác nhận đúng là người đó, và session để sau đó server vẫn nhận ra cùng một người. Các sơ đồ cho thấy hash và Cookie mỗi thứ đảm nhận việc gì.

Bài viết này nói về cách chức năng đăng nhập hoạt động.

Chức năng đăng nhập gồm hai cơ chế: một cơ chế xác nhận người dùng đúng là người họ khai, và một cơ chế tiếp tục nhận ra chính người đó về sau.

"Băm (hash)", "phương thức session" và "JWT" là cách gọi cùng một chức năng đăng nhập ở từng tình huống khác nhau.

Chức năng đăng nhập gồm hai cơ chế
tanakađăng nhậpXác nhậnđúng là người đóTiếp tục nhận racùng một ngườiSo sánh hashmật khẩuSession IDtrong Cookie
Một lần đăng nhập ở bên trái tách thành nhánh trên và nhánh dưới. Nhánh trên là bước kiểm tra chỉ làm một lần lúc đầu, nhánh dưới là bước kiểm tra làm lại ở mọi request sau đó. Cột bên phải là các thuật ngữ bài này đề cập.

Nhánh trên là bước kiểm tra chỉ làm một lần, nhánh dưới là bước kiểm tra làm ở mọi request sau đó.

Xác thực là xác nhận bạn là ai, phân quyền là quyết định bạn được làm gì

Xác thực (authentication: xác nhận người dùng đúng là người họ khai) là phán đoán được thực hiện lúc đăng nhập.

Phân quyền (authorization: quyết định người dùng đã được xác nhận thì được làm gì) là một phán đoán khác, diễn ra sau đó.

Hai phán đoán server thực hiện trong một request xóa đặt chỗ
Một request "xóa đặt chỗ này"
Phán đoán thứ nhất — xác thực (có đúng là tanaka không)
  • Căn cứ — ID và mật khẩu; sau khi đăng nhập là session ID trong Cookie
  • Khi không qua — đưa về màn hình đăng nhập
  • Mật khẩu chỉ được kiểm tra một lần, sau đó là session ID
Phán đoán thứ hai — phân quyền (có được xóa đặt chỗ này không)
  • Căn cứ — ai là chủ của đặt chỗ, người dùng có phải quản trị viên không
  • Khi không qua — trả về "bạn không có quyền"
  • Kiểm tra lại mỗi lần thao tác
Khung ngoài là một request. Bên trong có hai phán đoán, xếp theo thứ tự từ trên xuống. Dù qua được phán đoán trên, phán đoán dưới vẫn có thể từ chối.

Dù cùng là thao tác của tanaka, hai phán đoán vẫn được quyết định riêng.

Cùng là tanaka nhưng có thể bị chặn ở xác thực hoặc ở phân quyền
Chưa đăng nhập,mở danh sách đặt chỗĐã đăng nhập,xóa đặt chỗ của mìnhĐã đăng nhập,xóa đặt chỗ người khácKhông quaxác thựcQua xác thựcQua xác thựcKhông tớiphán đoánĐúng chủ sở hữu→ cho phépChủ là người khác→ từ chốiMàn hình đăng nhậpĐã xóaKhông có quyền
Mỗi hàng là một thao tác. Đi từ trái qua xác thực rồi tới phân quyền, và tại điểm không qua được thì màn hình bên phải được trả về. Hàng trên cùng không đi tới phân quyền.

Có hàng dừng ở xác thực, có hàng qua xác thực rồi dừng ở phân quyền.

Các thiết lập kiểu "chỉ quản trị viên dùng được", "chỉ xem được dữ liệu của mình" chính là phân quyền.

Xác thực là xác nhận bạn là ai, phân quyền là quyết định bạn được làm gì

Việc đăng nhập được và việc được phép thực hiện thao tác là hai điều được quyết định riêng.

Xác nhận người dùng có đúng là người họ khai là xác thực, quyết định người đó có được thực hiện thao tác đó không là phân quyền, và phân quyền được server kiểm tra lại mỗi lần thao tác.

Lưu mật khẩu dưới dạng hash, rồi so hash với hash

Điều đầu tiên cần nắm về xác thực là server không lưu chính mật khẩu.

Thứ được lưu là hash (hash: chuỗi ký tự tạo ra từ chuỗi gốc theo một quy trình cố định và không khôi phục lại được chuỗi gốc).

Ký tự bạn nhậpBăm ngay tại chỗSo với hash đã lưuKết quả
hanabi2026 (đúng)a3f9...c1Giống a3f9...c1Đăng nhập thành công
hanabi2025 (khác 1 ký tự)7b20...e4Khác a3f9...c1Đăng nhập thất bại
Hanabi2026 (khác chữ hoa)d15c...8aKhác a3f9...c1Đăng nhập thất bại

Chỉ khác một ký tự thôi thì hash tạo ra đã hoàn toàn khác.

Việc kiểm tra vẫn làm được dù hash không khôi phục lại được, vì thứ đem ra so cũng là hash.

Thứ server nắm giữ chỉ là một chuỗi không khôi phục được.

Những gì được lưu trong cơ sở dữ liệu và những gì không
Cơ sở dữ liệu của my-app
Một hàng trong bảng users (tanaka)
  • email — tanaka@example.com
  • password_hash — a3f9...c1
  • created_at — ngày giờ tài khoản được tạo
Những gì hàng này không chứa
  • Chuỗi ký tự đã nhập hanabi2026
  • Dữ liệu ở dạng khôi phục lại được ký tự đã nhập
  • Trạng thái đang đăng nhập hay không
Bên trong cơ sở dữ liệu của my-app. Một hàng của tanaka chỉ chứa địa chỉ email, hash và ngày giờ đăng ký.

Mật khẩu được lưu dưới dạng hash để phòng khi nội dung cơ sở dữ liệu bị lộ.

Những chuỗi hay được dùng thì đoán ra được, nên trước khi băm sẽ thêm vào một chuỗi khác nhau theo từng người dùng (salt).

HTTPS lúc gửi là biện pháp để dữ liệu không bị đọc trên đường đi, khác với chuyện lưu ở dạng nào.

Hash có khớp hay không quyết định điều xảy ra tiếp theo
tanaka nhấnĐăng nhậpSo sánhhashKhớpKhông khớpTạo bản ghi,trả về session IDTrả lại màn hìnhđăng nhập
Một lần đăng nhập ở bên trái tách lên trên hoặc xuống dưới theo kết quả so sánh. Chỉ khi khớp thì bên trong server mới tạo ra một bản ghi.

Thứ được lưu là hash, thứ đem so cũng là hash

Server không giữ mật khẩu mà người dùng đã nhập.

Thứ server giữ chỉ là một chuỗi không khôi phục được, và mỗi lần đăng nhập nó đưa ký tự vừa nhập qua đúng quy trình đó rồi xem chuỗi tạo ra có giống chuỗi đã lưu hay không.

Sau khi đăng nhập, server biết vẫn là cùng một người nhờ session ID trong Cookie

Dù đăng nhập thành công, request mở trang tiếp theo vẫn là một request khác.

HTTP là stateless (một lần xử lý không nhớ lần xử lý trước), nên server không giữ thông tin ai vừa đăng nhập lúc nãy.

Việc đăng nhập đã qua được ghi lại ở phía server, và trình duyệt được giao một chuỗi trỏ tới bản ghi đó.

Bản ghi để lại ở phía server là session, chuỗi trỏ tới nó là session ID, còn cơ chế giao chuỗi đó cho trình duyệt và bắt gửi kèm mỗi lần là Cookie.

Sau khi đăng nhập, trình duyệt và server mỗi bên giữ những gì
my-app sau khi đăng nhập
Bên trong trình duyệt của tanaka
  • Cookie: session_id=3f9a1c...e81b
  • Tự động gắn kèm mọi request tới cùng site
  • Ngoài chuỗi này ra thì không giữ gì khác
Bên trong server
  • Bản ghi session 3f9a1c...e81b là tanaka
  • Bản ghi này có thời hạn hiệu lực
  • Đăng xuất thì bản ghi này bị xóa
Cùng một chuỗi 3f9a1c...e81b nối phía trình duyệt với bản ghi ở phía server. Tên và quyền chỉ có ở phía server.

Phía trình duyệt chỉ có chuỗi 3f9a1c...e81b, không chứa tên cũng không chứa quyền.

Đó là đăng nhập của ai thì chỉ biết được khi chuỗi đó được ghép với bản ghi ở phía server.

Chỉ hàng trên được ghép với bản ghi, nên danh sách đặt chỗ trả về mà không phải hỏi lại mật khẩu.

Việc trao nhận này diễn ra dưới dạng hai dòng sau.

# Dòng server trả về khi đăng nhập thành công
Set-Cookie: session_id=3f9a1c...e81b; HttpOnly; Secure

# Từ đó về sau, dòng được gắn vào mọi request trình duyệt gửi tới cùng site
Cookie: session_id=3f9a1c...e81b

Hai mục phía sau Set-Cookie là chỉ thị cho trình duyệt.

HttpOnly khiến JavaScript trên trang không đọc được Cookie này, còn Secure chỉ cho gửi khi dùng HTTPS.

Nội dung Cookie có thể bị người dùng sửa, nên tin ngay nội dung đó hay không sẽ cho kết quả khác nhau.

Tin ngay giá trị Cookie hay không sẽ cho kết quả khác nhau
is_admin=true(tự khai là admin)được thêm rồi gửiTin ngay giá trịCookie nhận đượcMàn hình quản trịhiện raĐọc bản ghitừ session_idVẫn là người dùngthông thường
Khi một Cookie đã bị người dùng sửa đi tới server. Đường trên phán đoán theo giá trị nhận được, đường dưới phán đoán theo bản ghi trong server.

Cookie được lưu trong trình duyệt, nên người dùng có thể sửa nội dung rồi gửi đi.

Trong Cookie chỉ đặt session ID, còn phán đoán thì dựa vào bản ghi ở phía server.

Trong khung bên trái chỉ đặt một chuỗi không mang ý nghĩa gì.

Mọi căn cứ để quyết định đó là ai và được làm gì đều nằm trong khung bên phải.

Trình duyệt chỉ được giao một chuỗi trỏ tới bản ghi

Ngay lúc request đi tới, server không biết đó là request của ai.

Server biết được là nhờ bản ghi tạo lúc đăng nhập và chuỗi trình duyệt gửi mỗi lần được ghép với nhau, còn nếu bản ghi bị xóa hoặc hết hạn thì dù gửi cùng chuỗi đó bạn vẫn quay về màn hình đăng nhập.

Khác nhau giữa phương thức session và JWT — server giữ bản ghi, hay chuỗi tự giữ

"Phương thức session hay JWT" là lựa chọn xem bên nào giữ bằng chứng đã đăng nhập.

Cách còn lại là không để bản ghi trên server mà giao chính bằng chứng đó cho trình duyệt.

Chuỗi bằng chứng được gắn kèm và gửi mỗi lần là token, và JWT (JSON Web Token: chuỗi gói chung ID người dùng, thời hạn hiệu lực và chữ ký) là định dạng tiêu biểu.

Bên trong một JWT mà trình duyệt được giao
Một chuỗi trình duyệt gắn kèm và gửi mỗi lần (JWT)
Nội dung — của ai, có hiệu lực tới khi nào
  • ID người dùng của tanaka
  • Thời hạn hiệu lực (dùng được tới thời điểm này)
Chữ ký — giá trị chỉ server mới tạo được
  • Chuỗi tính ra từ nội dung
  • Sửa nội dung thì phép tính không còn khớp
Khung ngoài là một chuỗi được gửi mỗi lần. Nội dung nằm bên trong ở dạng đọc là hiểu, và chữ ký là thứ phát hiện việc sửa đổi.

Nhờ có chữ ký, server biết chuỗi nhận được là do chính mình phát hành mà không cần đối chiếu với bản ghi.

Khía cạnhPhương thức sessionJWT
Thứ được giao khi đăng nhập thành côngsession_id=3f9a1c...e81beyJhbGci... ID, thời hạn và chữ ký
Nơi đặt căn cứ để phán đoánBản ghi bên trong serverNội dung bên trong chuỗi
Cách kiểm tra mỗi lầnĐối chiếu với bản ghiXác minh chữ ký

JWT tự mang nội dung nên được chấp nhận mà không cần bản ghi trên server.

Với phương thức session, xóa bản ghi là request tiếp theo không qua được nữa.

JWT qua được cho tới khi hết hạn, nên khi muốn chặn ngay thì bạn giữ riêng một danh sách các token đã thu hồi.

Bài này vẽ phương thức session dưới dạng Cookie và JWT dưới dạng chuỗi, nhưng thực tế cũng có cấu hình đặt JWT vào trong Cookie.

Dù chỗ đặt có đổi, khác biệt vẫn nằm ở chỗ đối chiếu với bản ghi hay kiểm tra chữ ký.

Server giữ bản ghi, hay chuỗi tự giữ luôn nội dung

Ở cả hai phương thức, việc trình duyệt gắn kèm và gửi một chuỗi mỗi lần là giống nhau.

Khác nhau là server làm gì khi nhận được: phương thức session đi tra bản ghi của chính nó, còn JWT kiểm tra chữ ký gắn trên chuỗi.

QUIZ

Kiểm tra kiến thức

Hãy trả lời từng câu hỏi một.

Câu 1Khi server lưu mật khẩu của người dùng, thứ được đặt trong cơ sở dữ liệu là gì?

Câu 2Khi bạn mở một trang khác sau khi đăng nhập, vì sao server biết vẫn là cùng người dùng đó?

Câu 3Một người dùng đã đăng nhập định xóa đặt chỗ của người khác và bị server từ chối. Đây là phán đoán nào?