Tôi có một dự án dựa trên web cho phép người dùng làm việc cả trực tuyến và ngoại tuyến và tôi đang tìm cách tạo id duy nhất cho các bản ghi ở phía máy khách. Tôi muốn một cách tiếp cận hoạt động trong khi người dùng ngoại tuyến (nghĩa là không thể nói chuyện với máy chủ), được đảm bảo là duy nhất và an toàn. Bằng cách "bảo mật", tôi đặc biệt lo lắng về việc khách hàng gửi các id trùng lặp (độc hại hoặc theo cách khác) và do đó phá hoại tính toàn vẹn dữ liệu.
Tôi đã làm một số việc, hy vọng điều này đã được giải quyết. Tôi đã không tìm thấy bất cứ điều gì rất dứt khoát, đặc biệt là về phương pháp tiếp cận được sử dụng trong các hệ thống sản xuất. Tôi đã tìm thấy một số ví dụ cho các hệ thống mà người dùng sẽ chỉ truy cập dữ liệu mà họ đã tạo (ví dụ: danh sách Todo được truy cập trên nhiều thiết bị, nhưng chỉ bởi người dùng đã tạo dữ liệu đó). Thật không may, tôi cần một cái gì đó tinh vi hơn một chút. Tôi đã tìm thấy một số ý tưởng thực sự tốt ở đây , phù hợp với cách tôi nghĩ mọi thứ có thể hoạt động.
Dưới đây là giải pháp đề xuất của tôi.
Một số yêu cầu
- ID phải là duy nhất trên toàn cầu (hoặc ít nhất là duy nhất trong hệ thống)
- Được tạo trên máy khách (tức là thông qua javascript trong trình duyệt)
- An toàn (như đã nêu ở trên và mặt khác)
- Dữ liệu có thể được xem / chỉnh sửa bởi nhiều người dùng, bao gồm cả những người dùng không tạo ra nó
- Không gây ra sự cố hiệu suất đáng kể cho db phụ trợ (như MongoDB hoặc CouchDB)
Giải pháp đề xuất
Khi người dùng tạo một tài khoản, họ sẽ được cung cấp một uuid do máy chủ tạo ra và được biết là duy nhất trong hệ thống. Id này KHÔNG phải giống với mã xác thực người dùng. Hãy gọi id này là "mã thông báo id" của người dùng.
Khi người dùng tạo một bản ghi mới, họ sẽ tạo một uuid mới trong javascript (được tạo bằng window.crypto khi có sẵn. Xem ví dụ tại đây ). Id này được nối với "mã thông báo id" mà người dùng nhận được khi họ tạo tài khoản của họ. Id tổng hợp mới này (mã thông báo id phía máy chủ + uuid phía máy khách) hiện là định danh duy nhất cho bản ghi. Khi người dùng trực tuyến và gửi bản ghi mới này đến máy chủ phụ trợ, máy chủ sẽ:
- Xác định đây là hành động "chèn" (nghĩa là không cập nhật hoặc xóa)
- Xác thực cả hai phần của khóa tổng hợp là uuids hợp lệ
- Xác thực rằng phần "mã thông báo id" được cung cấp của id tổng hợp là chính xác cho người dùng hiện tại (nghĩa là nó khớp với mã thông báo id mà máy chủ đã gán cho người dùng khi họ tạo tài khoản của họ)
- Nếu mọi thứ đều copasetic, chèn dữ liệu vào db (cẩn thận để làm một chèn và không phải là một "upsert" để nếu id không đã tồn tại nó không cập nhật một bản ghi hiện do nhầm lẫn)
Truy vấn, cập nhật và xóa sẽ không yêu cầu logic đặc biệt nào. Họ chỉ đơn giản là sử dụng id cho bản ghi theo cách tương tự như các ứng dụng truyền thống.
Những lợi thế của phương pháp này là gì?
Mã khách hàng có thể tạo dữ liệu mới khi ngoại tuyến và biết id cho bản ghi đó ngay lập tức. Tôi đã xem xét các cách tiếp cận thay thế trong đó một id tạm thời sẽ được tạo trên máy khách mà sau đó sẽ được đổi thành id "cuối cùng" khi hệ thống trực tuyến. Tuy nhiên, điều này cảm thấy rất giòn. Đặc biệt là khi bạn bắt đầu nghĩ về việc tạo dữ liệu con bằng khóa ngoại cũng cần phải cập nhật. Chưa kể xử lý các url sẽ thay đổi khi id thay đổi.
Bằng cách biến id thành tổng hợp của giá trị do khách hàng tạo và giá trị do máy chủ tạo, mỗi người dùng sẽ tạo id một cách hiệu quả trong hộp cát. Điều này nhằm hạn chế thiệt hại có thể được thực hiện bởi một khách hàng độc hại / lừa đảo. Ngoài ra, mọi va chạm id đều trên cơ sở mỗi người dùng, không phải toàn cầu đối với toàn bộ hệ thống.
Vì mã thông báo id người dùng được gắn với tài khoản của họ, id chỉ có thể được tạo trong hộp cát người dùng bởi các máy khách được xác thực (tức là nơi người dùng đăng nhập thành công). Điều này nhằm ngăn khách hàng độc hại tạo id xấu cho người dùng. Tất nhiên, nếu mã thông báo xác thực người dùng bị đánh cắp bởi một khách hàng độc hại, họ có thể làm điều xấu. Nhưng, một khi mã thông báo xác thực đã bị đánh cắp, tài khoản sẽ bị xâm phạm. Trong trường hợp điều này xảy ra, thiệt hại sẽ được giới hạn ở tài khoản bị xâm nhập (không phải toàn bộ hệ thống).
Mối quan tâm
Dưới đây là một số mối quan tâm của tôi với phương pháp này
Điều này sẽ tạo ra các id đủ độc đáo cho một ứng dụng quy mô lớn? Có bất kỳ lý do để nghĩ rằng điều này sẽ dẫn đến va chạm id? Javascript có thể tạo ra một uuid đủ ngẫu nhiên để làm việc này không? Có vẻ như window.crypto có sẵn khá rộng rãi và dự án này đã yêu cầu các trình duyệt hiện đại hợp lý. ( câu hỏi này hiện có một câu hỏi SO riêng )
Có bất kỳ sơ hở nào mà tôi thiếu có thể cho phép người dùng độc hại thỏa hiệp hệ thống không?
Có lý do để lo lắng về hiệu suất DB khi truy vấn khóa tổng hợp gồm 2 uuids. Làm thế nào id này nên được lưu trữ để có hiệu suất tốt nhất? Hai trường riêng biệt hoặc một trường đối tượng duy nhất? Sẽ có một cách tiếp cận "tốt nhất" khác cho Mongo vs Couch? Tôi biết rằng việc có một khóa chính không tuần tự có thể gây ra các vấn đề hiệu suất đáng chú ý khi thực hiện chèn. Sẽ thông minh hơn khi có một giá trị được tạo tự động cho khóa chính và lưu trữ id này dưới dạng một trường riêng biệt? ( câu hỏi này hiện có một câu hỏi SO riêng )
Với chiến lược này, sẽ dễ dàng xác định rằng một bộ hồ sơ cụ thể đã được tạo bởi cùng một người dùng (vì tất cả họ đều chia sẻ cùng một mã thông báo id hiển thị công khai). Mặc dù tôi không thấy bất kỳ vấn đề tức thời nào với vấn đề này, tốt hơn hết là không rò rỉ thêm thông tin về các chi tiết nội bộ hơn mức cần thiết. Một khả năng khác là băm khóa tổng hợp, nhưng có vẻ như nó có thể rắc rối hơn giá trị của nó.
Trong trường hợp có xung đột id cho người dùng, không có cách nào đơn giản để khôi phục. Tôi cho rằng khách hàng có thể tạo ra một id mới, nhưng điều này có vẻ như rất nhiều công việc cho một trường hợp cạnh thực sự không nên xảy ra. Tôi đang có ý định rời bỏ điều này.
Chỉ người dùng được xác thực mới có thể xem và / hoặc chỉnh sửa dữ liệu. Đây là một giới hạn chấp nhận được cho hệ thống của tôi.
Phần kết luận
Là trên một kế hoạch hợp lý? Tôi nhận ra một số điều này dẫn đến một cuộc gọi phán xét dựa trên sự hiểu biết đầy đủ hơn về ứng dụng được đề cập.