Как вы даете своим пользователям уникальный идентификатор без использования первичного ключа в базе данных?

Если у меня 10 000 пользователей, а первичный ключ - это уникальный идентификатор от 1 до 10 000, есть ли способ дать им всем уникальный идентификатор, чтобы исходный первичный ключ не мог быть выведен из него?

Например, ссылка на ваш профиль на Facebook или аналогичную страницу будет http://site.com/profile?id=293852

Вполне вероятно, что идентификатор там совпадает с первичным ключом их пользователя в базе данных? Я изо всех сил пытаюсь придумать способ иметь два несвязанных столбца уникальных идентификаторов, потому что случайно сгенерированные должны быть уникальными. Я представляю, если бы можно было иметь GUID, используя числа, только длина была бы слишком большой.

А идеи?

5 ответов

Решение

У вас есть, как правило, два варианта:

  1. Как вы сказали, используйте случайно сгенерированные данные. (Вам нужно только убедиться, что они уникальны, то есть либо достаточно длинные, либо генерировать-проверить-повторить.)
  2. Получите первичный ключ и преобразуйте его "псевдослучайно" во что-то еще, что, по- видимому, не имеет ничего общего с первичным ключом. Преобразование может быть очень простым (если вы хотите просто мягкую защиту), например new Random(primaryKey).NextInt()или это может быть довольно сложно, но защищено от атак, например, любое шифрование с сохранением формата.

Но тогда... как вы думаете, почему вы должны защищать значения ваших первичных ключей? Если единственная причина заключается в том, что пользователи не могут угадать другие действительные идентификаторы пользователей, вы можете просто добавить случайную строку в первичный ключ (и сохранить ее в базе данных и проверить ее правильность при доступе).

Из соображений безопасности настоятельно рекомендуется сделать идентификатор не последовательным, чтобы избежать перечисления пользователя в системе. Но 4 миллиарда (я имею в виду 2^32) слишком малы, чтобы обеспечить недопустимый интервал. Вот почему GUID является более предпочтительным. В зависимости от базы данных (с точки зрения вашей спецификации это похоже на MSSQL) вы можете хранить в guid-подобных полях, байтовых полях (для MySQL) или в 2 отдельных int64.

Чтобы уменьшить размер URL, можно применить кодировку base64, чтобы GUID выглядел короче.

Как генерировать случайные и уникальные идентификаторы - полезный вопрос, но вы, похоже, делаете предположение о том, когда их генерировать!

Я хочу сказать, что вам не нужно генерировать эти идентификаторы во время создания ваших строк, потому что они практически не зависят от вставляемых данных.

То, что я делаю, - это предварительно генерирую случайные идентификаторы для использования в будущем, таким образом, я могу использовать свое приятное время и абсолютно гарантировать, что они уникальны, и во время вставки обработки не требуется.

Например, у меня есть таблица заказов с указанием order_id. Этот идентификатор генерируется на лету, когда пользователь вводит заказ, постепенно 1,2,3 и т. Д. Навсегда. Пользователю не нужно видеть этот внутренний идентификатор.

Тогда у меня есть другая таблица - random_ids с (order_id, random_id). У меня есть подпрограмма, которая запускается каждую ночь, которая предварительно загружает эту таблицу достаточным количеством строк, чтобы покрыть заказы, которые могут быть вставлены в следующие 24 часа. (Если я когда-нибудь получу 10000 заказов за один день, у меня возникнут проблемы, но это будет хорошей проблемой!)

Этот подход гарантирует уникальность и отводит любую нагрузку обработки от транзакции вставки и в пакетную процедуру, где это не влияет на пользователя.

Я использую часть GUID и фактический идентификатор.

В таблице у меня есть тип столбца uniqueidentifier со значением по умолчанию newid()

Затем я беру часть этого и добавляю фактический серийный идентификатор в конце с известным разделителем между ними. Я использую букву H, поскольку она не отображается в GUID.

Так что для строки № 8659 у меня будет:
IDcolumn = 8659
GUIDcolumn = '{200BAB55-C7D5-4456-AB57-CFF8B7E82A90}'
PROFILECODE = '200BAB55H8659'

Я могу найти правильную строку:

partGUID=split(PROFILECODE,'H')(0) - gives 200BAB55
realID=split(PROFILECODE,'H')(1) - give 8659
select * from mytable where IDcolumn=8659 and left(GUIDcolumn,8)='200BAB55';

В теории парсер SQL должен сначала найти все строки с IDcolumn 8659, а затем проверить GUIDcolumn

Если люди пытаются угадать идентификатор для профиля, они не могут просто изменить одну его часть и добиться успеха.

Что плохого в том, чтобы позволить пользователю видеть первичный ключ?

Вы можете генерировать числа случайным образом, удостовериться, что это действительно большое число, чтобы столкновения были маловероятными, а затем просто запустить выбор, чтобы проверить, что он не существует.

Или, вы можете выбрать огромное количество, а затем основать некоторое уравнение вокруг этого. Что-то вроде:

unique = 1000000000 * (-1 * PK)^3

Это означает, что уникальные числа будут удаляться от вашего стартового номера по мере увеличения PK, и будут выше или ниже его в зависимости от того, является ли PK нечетным или четным. Чем сложнее вы добавляете к уравнению, тем меньше вероятность того, что оно будет обнаружено, но никогда не будет на 100% полагаться на этот метод, так как всегда есть вероятность, что кто-то с ним справится.

Другие вопросы по тегам