Ich habe ein webbasiertes Projekt, mit dem Benutzer sowohl online als auch offline arbeiten können, und ich suche nach einer Möglichkeit, eindeutige IDs für Datensätze auf der Clientseite zu generieren. Ich hätte gerne einen Ansatz, der funktioniert, wenn ein Benutzer offline ist (dh nicht mit einem Server kommunizieren kann), der garantiert eindeutig und sicher ist. Mit "sicher" befasse ich mich speziell mit Kunden, die doppelte IDs (böswillig oder auf andere Weise) übermitteln und dadurch die Datenintegrität in Mitleidenschaft ziehen.
Ich habe ein bisschen gegoogelt und gehofft, dass dies bereits ein gelöstes Problem war. Ich habe nichts Bestimmtes gefunden, insbesondere in Bezug auf Ansätze, die in Produktionssystemen verwendet werden. Ich habe einige Beispiele für Systeme gefunden, bei denen Benutzer nur auf die von ihnen erstellten Daten zugreifen (z. B. eine Aufgabenliste, auf die auf mehreren Geräten zugegriffen wird, jedoch nur von dem Benutzer, der sie erstellt hat). Leider brauche ich etwas ausgefeilteres. Ich habe hier ein paar wirklich gute Ideen gefunden , die meiner Meinung nach funktionieren könnten.
Unten ist meine vorgeschlagene Lösung.
Einige Anforderungen
- IDs sollten global eindeutig sein (oder zumindest innerhalb des Systems eindeutig sein)
- Auf dem Client generiert (zB über Javascript im Browser)
- Sicher (wie oben beschrieben und ansonsten)
- Daten können von mehreren Benutzern angezeigt / bearbeitet werden, auch von Benutzern, die sie nicht verfasst haben
- Verursacht keine signifikanten Leistungsprobleme für Backend-Datenbanken (wie MongoDB oder CouchDB)
Vorgeschlagene Lösung
Wenn Benutzer ein Konto erstellen, erhalten sie eine UUID, die vom Server generiert wurde und die im System eindeutig ist. Diese ID darf NICHT mit dem Authentifizierungstoken des Benutzers identisch sein. Nennen wir diese ID die Benutzer "ID-Token".
Wenn ein Benutzer einen neuen Datensatz erstellt, generiert er eine neue UUID in Javascript (wenn verfügbar mit window.crypto erstellt. Beispiele finden Sie hier ). Diese ID wird mit dem "ID-Token" verknüpft, das der Benutzer beim Erstellen seines Kontos erhalten hat. Diese neue zusammengesetzte ID (serverseitiges ID-Token + clientseitige UUID) ist jetzt die eindeutige ID für den Datensatz. Wenn der Benutzer online ist und diesen neuen Datensatz an den Back-End-Server sendet, führt der Server Folgendes aus:
- Identifizieren Sie dies als "Einfügen" -Aktion (dh keine Aktualisierung oder Löschung).
- Überprüfen Sie, ob beide Teile des zusammengesetzten Schlüssels gültige UUIDs sind
- Vergewissern Sie sich, dass der angegebene "ID-Token" -Teil der zusammengesetzten ID für den aktuellen Benutzer korrekt ist (dh er entspricht dem ID-Token, das der Server dem Benutzer beim Erstellen seines Kontos zugewiesen hat).
- Wenn alles copasetic ist, legen Sie die Daten in den db (wobei darauf geachtet , einen Einsatz und keine „Upsert“ zu tun , so dass , wenn die ID hat bereits existiert es nicht einen vorhandenen Datensatz versehentlich nicht aktualisiert)
Abfragen, Aktualisierungen und Löschvorgänge erfordern keine spezielle Logik. Sie würden einfach die ID für den Datensatz auf die gleiche Weise wie herkömmliche Anwendungen verwenden.
Was sind die Vorteile dieses Ansatzes?
Der Client-Code kann offline neue Daten erstellen und kennt die ID für diesen Datensatz sofort. Ich habe alternative Ansätze in Betracht gezogen, bei denen auf dem Client eine temporäre ID generiert wird, die später gegen eine "endgültige" ID ausgetauscht wird, wenn das System online ist. Dies fühlte sich jedoch sehr spröde an. Insbesondere, wenn Sie darüber nachdenken, untergeordnete Daten mit Fremdschlüsseln zu erstellen, die ebenfalls aktualisiert werden müssten. Ganz zu schweigen vom Umgang mit URLs, die sich ändern würden, wenn sich die ID ändert.
Indem IDs aus einem vom Client generierten Wert UND einem vom Server generierten Wert zusammengesetzt werden, erstellt jeder Benutzer effektiv IDs in einer Sandbox. Dies soll den Schaden begrenzen, den ein böswilliger / betrügerischer Client anrichten kann. Außerdem sind ID-Kollisionen benutzerbezogen und nicht global für das gesamte System.
Da ein Benutzer-ID-Token an sein Konto gebunden ist, können IDs nur von Clients in einer Benutzersandbox generiert werden, die authentifiziert sind (dh bei denen sich der Benutzer erfolgreich angemeldet hat). Auf diese Weise sollen böswillige Clients daran gehindert werden, für einen Benutzer ungültige IDs zu erstellen. Wenn ein Benutzerauthentifizierungs-Token von einem böswilligen Client gestohlen wird, kann dies natürlich zu schlechten Ergebnissen führen. Sobald jedoch ein Authentifizierungs-Token gestohlen wurde, ist das Konto auf jeden Fall kompromittiert. In diesem Fall ist der verursachte Schaden auf das gefährdete Konto beschränkt (nicht auf das gesamte System).
Sorgen
Hier sind einige meiner Bedenken bezüglich dieses Ansatzes
Generiert dies ausreichend eindeutige IDs für eine Großanwendung? Gibt es einen Grund zu der Annahme, dass dies zu ID-Kollisionen führt? Kann Javascript eine ausreichend zufällige UUID generieren, damit dies funktioniert? Es sieht so aus, als ob window.crypto ziemlich weit verbreitet ist und für dieses Projekt sind bereits einigermaßen moderne Browser erforderlich. ( Diese Frage hat jetzt eine eigene SO-Frage )
Gibt es Lücken, die mir fehlen und die es einem böswilligen Benutzer ermöglichen könnten, das System zu gefährden?
Gibt es Grund zur Besorgnis über die DB-Leistung, wenn Sie nach einem zusammengesetzten Schlüssel fragen, der aus 2 UUIDs besteht. Wie sollte diese ID gespeichert werden, um die bestmögliche Leistung zu erzielen? Zwei getrennte Felder oder ein einzelnes Objektfeld? Gibt es einen anderen "besten" Ansatz für Mongo vs Couch? Ich weiß, dass ein nicht sequentieller Primärschlüssel beim Einfügen erhebliche Leistungsprobleme verursachen kann. Wäre es sinnvoller, einen automatisch generierten Wert für den Primärschlüssel zu haben und diese ID als separates Feld zu speichern? ( Diese Frage hat jetzt eine eigene SO-Frage )
Mit dieser Strategie lässt sich leicht feststellen, dass ein bestimmter Datensatzsatz von demselben Benutzer erstellt wurde (da alle denselben öffentlich sichtbaren ID-Token verwenden). Obwohl ich keine unmittelbaren Probleme damit sehe, ist es immer besser, nicht mehr Informationen über interne Details zu veröffentlichen, als benötigt werden. Eine andere Möglichkeit wäre das Hashing des zusammengesetzten Schlüssels, aber das scheint mehr Mühe zu bereiten, als es wert ist.
Im Falle einer ID-Kollision für einen Benutzer gibt es keine einfache Möglichkeit zur Wiederherstellung. Ich nehme an, der Client könnte eine neue ID generieren, aber dies scheint eine Menge Arbeit für einen Edge-Fall zu sein, der eigentlich nie passieren sollte. Ich habe vor, dies unadressiert zu lassen.
Nur authentifizierte Benutzer können Daten anzeigen und / oder bearbeiten. Dies ist eine akzeptable Einschränkung für mein System.
Fazit
Steht über einem vernünftigen Plan? Mir ist klar, dass ein Teil davon auf eine Entscheidung zurückzuführen ist, die auf einem umfassenderen Verständnis des betreffenden Antrags beruht.