CodeGym /Các khóa học /SQL SELF /Kiểm soát truy cập cấp dòng: Row-Level Security (RLS)

Kiểm soát truy cập cấp dòng: Row-Level Security (RLS)

SQL SELF
Mức độ , Bài học
Có sẵn

Nếu quyền ở cấp role và bảng giống như "bảo vệ ở cửa chính", thì Row-Level Security giống như bảo vệ riêng kiểm tra xem từng người dùng có được vào từng phòng riêng trong tòa nhà không.

RLS giúp đảm bảo người dùng chỉ làm việc với những dòng trong bảng mà họ được phép. Ví dụ:

  • Trong shop online, manager chỉ nên thấy đơn hàng của mình.
  • Trong CRM-platform, nhân viên chỉ thấy khách hàng của team mình.
  • Trong hệ thống ngân hàng, khách chỉ được truy cập tài khoản của chính họ.

RLS hoạt động như thế nào

RLS dựa trên khái niệm policy truy cập. Policy xác định dòng nào trong bảng sẽ hiển thị cho role hoặc user nào, và dòng nào được phép sửa (INSERT, UPDATE, DELETE).

Mặc định RLS bị tắt cho tất cả các bảng, và bạn phải tự bật nó lên.

Bật RLS cho bảng

Giờ mình tạo bảng orders, nơi lưu đơn hàng của shop online:

CREATE TABLE orders (
    order_id SERIAL PRIMARY KEY,
    user_id INT NOT NULL,
    product_name TEXT NOT NULL,
    price NUMERIC NOT NULL
);

Thêm vài dòng dữ liệu test:

INSERT INTO orders (user_id, product_name, price)
VALUES
    (1, 'Điện thoại thông minh', 500),
    (2, 'Laptop', 1000),
    (1, 'Tai nghe', 100),
    (3, 'Bàn phím', 50);

Bây giờ bật RLS cho bảng này:

ALTER TABLE orders ENABLE ROW LEVEL SECURITY;

Mình vừa làm gì? Đã kích hoạt cơ chế RLS, nhưng chưa có policy nào cả. Khi chưa có policy, RLS thực ra chưa ảnh hưởng gì đến quyền truy cập bảng.

Tạo policy truy cập

Để thêm rule kiểm soát truy cập, dùng lệnh:

CREATE POLICY ten_policy
ON ten_bang
[FOR { SELECT | INSERT | UPDATE | DELETE }]
TO role
USING (dieu_kien_truy_cap)
WITH CHECK (dieu_kien_kiem_tra);
  • FOR: xác định policy áp dụng cho thao tác nào (SELECT, INSERT, UPDATE, DELETE). Nếu bỏ qua, policy áp dụng cho tất cả thao tác.
  • TO: chỉ định policy này dành cho role nào. Nếu không ghi, policy áp dụng cho mọi role.
  • USING: điều kiện để dòng được nhìn thấy bởi user.
  • WITH CHECK: điều kiện kiểm tra cho thao tác INSERTUPDATE.

Ví dụ: chỉ truy cập đơn hàng của chính mình

Tạo policy cho phép user chỉ xem đơn hàng của mình. Giả sử id user hiện tại trùng với user_id trong bảng:

CREATE POLICY user_can_view_own_orders
ON orders
FOR SELECT
USING (user_id = current_user::INT);

Ở đây có gì?

  • Policy tên là user_can_view_own_orders.
  • Áp dụng cho thao tác SELECT.
  • Chỉ dòng nào có user_id trùng với id user hiện tại (current_user) mới thấy được.

Vậy nếu bạn đăng nhập với user_id = 1, bạn chỉ thấy đơn hàng của mình thôi.

Kiểm tra hoạt động của RLS

Tạo hai user: user1user2.

CREATE ROLE user1 LOGIN PASSWORD 'password1';
CREATE ROLE user2 LOGIN PASSWORD 'password2';

Cấp quyền truy cập bảng cho role:

GRANT SELECT ON orders TO user1, user2;

Bây giờ chuyển sang user user1 và thử query:

SELECT * FROM orders;

Kết quả: bạn chỉ thấy dòng có user_id = 1.

Policy cho INSERT

Giả sử bạn muốn user chỉ được thêm đơn hàng của chính mình (tức là dòng có user_id phải trùng với id user hiện tại).

Tạo policy cho INSERT:

CREATE POLICY user_can_insert_own_orders
ON orders
FOR INSERT
WITH CHECK (user_id = current_user::INT);

Bây giờ, nếu user1 cố thêm đơn hàng mà user_id khác 1, query sẽ báo lỗi.

Policy cho UPDATE và DELETE

Tương tự, bạn có thể tạo policy cho sửa và xóa dữ liệu. Ví dụ:

Để update dữ liệu của chính mình:

CREATE POLICY user_can_update_own_orders
ON orders
FOR UPDATE
USING (user_id = current_user::INT)
WITH CHECK (user_id = current_user::INT);

Để xóa dữ liệu của chính mình:

CREATE POLICY user_can_delete_own_orders
ON orders
FOR DELETE
USING (user_id = current_user::INT);

Áp dụng nhiều policy

Bạn có thể tạo nhiều policy cho một bảng. Tất cả sẽ được áp dụng cùng lúc. Ví dụ, nếu một role có nhiều rule, PostgreSQL sẽ kiểm tra hết (AND logic).

Kiểm tra và debug RLS

Để kiểm tra policy nào đã set cho bảng, dùng lệnh:

\di+ ten_bang

Nếu muốn tạm thời tắt RLS (ví dụ cho admin), chạy:

ALTER TABLE orders DISABLE ROW LEVEL SECURITY;

Role admin SUPERUSER mặc định không bị giới hạn bởi RLS, nên luôn thấy hết dữ liệu.

Lỗi thường gặp khi setup RLS

Lỗi có thể xảy ra nếu bạn quên:

  • Bật RLS bằng lệnh ALTER TABLE ... ENABLE ROW LEVEL SECURITY.
  • Tạo đủ policy cho mọi thao tác (SELECT, INSERT, UPDATE, DELETE).
  • Ghi đúng điều kiện trong USINGWITH CHECK. Ví dụ, nếu không kiểm tra user_id, có thể vô tình mở quyền cho tất cả dòng.

Row-Level Security là một trong những công cụ bảo mật mạnh nhất của PostgreSQL. Nó cho phép kiểm soát truy cập chi tiết đến từng dòng và tự động hóa việc quản lý quyền, rất quan trọng cho app phức tạp cần bảo vệ dữ liệu cao.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION