Không lưu phân quyền vô cookie, chỉ nên sử dụng cookie cho việc đăng nhập lại sau khi session hết hạn.
Không nên dùng captcha để bảo mật form. Đúng là có tác dụng thật, nhưng sẽ gây phiền phức cho người dùng.
Mình sẽ ví dụ cho bạn mẫu mình hay dùng, mẫu này đã được tối giản để bạn dễ theo dõi.
Database
-
Roles: id, name
Dùng để chia cấp bậc như Admin, Manager, Member.
-
Users: id, id_role, username, password
Thông tin người dùng.
-
RememberTokens: id_user, token, user_agent
Lưu token sau khi đăng nhập thành công.
-
Permissions: id_role, controller, action
Xác định quyền theo cấp bậc.
Đăng nhập lần đầu
Khi người dùng đăng nhập thành công, bạn lấy username + password + user_agent(thông tin trình duyệt) và mã hóa chúng để tạo token, có thể dùng md5 để mã hóa.
Lưu vào cookie id_user và token vừa tạo. Đồng thời cũng ghi vào RememberTokens.
Tạo session auth chứa id_user và id_role.
Phân quyền bằng ACL
ACL có thể lưu trong cache là tốt nhất, nếu không được thì lưu thành file, dù sao vẫn sẽ tốt hơn truy cập vào database.
Khi chưa có ACL thì bạn truy cập Permissions để lấy và tạo ACL chứa toàn bộ dữ liệu của nó. Ví dụ thế này:
admin|products|seach
admin|products|create
admin|products|update
admin|products|delete
manager|products|seach
manager|products|create
manager|products|update
member|products|seach
Khi người dùng thực hiện thao tác nào đó ta sẽ có được controller và action, thêm id_role trong session nữa là đủ để bạn so sánh với ACL. Nếu không tồn tại trong ACL thì cấm hết.
Đăng nhập trở lại bằng cookie
Session hết hạn, và cookie vẫn còn.
Lấy id_user từ cookie, kiểm tra tồn tại trong Users và lấy được thông tin người dùng.
Dùng username và password vừa lấy, cộng thêm user_agent của trình duyệt hiện tại là bạn tạo được token.
Kiểm tra token vừa tạo đó với cái đang lưu trong cookie.
Có được token và id_user rồi thì kiểm tra RememberTokens.
Khi các thông tin đã đúng thì tạo lại session auth.
Đăng xuất
Người dùng đăng xuất thì xóa cookie, session và dữ liệu trong RememberTokens.
Chống CSRF
Tạo chuỗi ngẫu nhiên mỗi khi nhập form, đồng thời ghi vào form nhập liệu của người dùng. Ví dụ: abcXYZ
<input type="hidden" name="token" value="abcXYZ">
Khi người dùng gửi request thì kiểm tra token, đồng thời xóa nó khỏi session.
Ví dụ đơn giản để bạn hình dung, chứ thực tế không nên dùng như vậy. Ngoài token người ta còn tạo thêm session token_time giới hạn thời gian. Còn token thì dùng các hàm băm như bcrypt để mã hóa, bạn sẽ có 2 chuỗi token khác nhau cho session và form, an toàn hơn.
Hiểu nguyên tắc thì có thể viết được, nhưng bạn nên dùng các thư viện đã được nhiều người kiểm chứng. Vừa bảo mật, vừa tránh được vấn đề hiệu suất.