Php 创建人类可输入、非顺序唯一id的良好实践';s

Php 创建人类可输入、非顺序唯一id的良好实践';s,php,mysql,Php,Mysql,我需要为我们的用户创建一个唯一的id。我不想使用自动递增的id,因为我不希望用户能够猜测我们有多少用户,或者增长率是多少 UUID也不是一个真正的选项,因为用户必须在智能手机上重新键入该id 因此,我希望我能尽可能减少对数据库的“暴力强迫”,以找到未使用的ID。什么是明智的做法 谢谢大家! 您可以在函数上使用随机uid创建记录并更新字段。 将检查功能的唯一性: CREATE FUNCTION get_unique_uid_for_your_table() RETURNS VARCHAR(255)

我需要为我们的用户创建一个唯一的id。我不想使用自动递增的id,因为我不希望用户能够猜测我们有多少用户,或者增长率是多少

UUID也不是一个真正的选项,因为用户必须在智能手机上重新键入该id

因此,我希望我能尽可能减少对数据库的“暴力强迫”,以找到未使用的ID。什么是明智的做法


谢谢大家!

您可以在函数上使用随机uid创建记录并更新字段。 将检查功能的唯一性:

CREATE FUNCTION get_unique_uid_for_your_table()
RETURNS VARCHAR(255)
BEGIN
    DECLARE chars VARCHAR(48) DEFAULT '0123456789bcdfghjklmnopqrstvwxyz';
    DECLARE len INTEGER DEFAULT 8;
    DECLARE new_uid VARCHAR(255) DEFAULT '';
    DECLARE i INTEGER DEFAULT 0;

    WHILE LENGTH(new_uid) = 0 OR EXISTS(SELECT uid FROM your_table WHERE uid = new_uid) DO
            SET len = 8;
            SET new_uid = '';

            WHILE (len > 0) DO
                    SET len = len - 1;
                    SET new_uid = CONCAT(new_uid, SUBSTR(chars, FLOOR(LENGTH(chars)) * RAND(), 1));
            END WHILE;
    END WHILE;

    RETURN new_uid;
END //
对于现有内容,您可以运行以下操作:

UPDATE your_table SET uid = get_unique_uid_for_your_table();
在insert语句中,您可以键入以下内容:

INSERT INTO your_table
(
    #all_fields

    uid
)
VALUES
(
    #all_values

    get_unique_uid_for_your_table()
);

创建一个随机uniqe编号列表(可以使用PHP的
range()
shuffle()
函数),并将其存储在数据库中,甚至存储在txt文件中。确保列表足够长,以便能持续一段时间。 然后,无论何时需要新ID,只要从列表中弹出第一个值即可

$list = range(0,999999); //list now contains numbers from 0 to 999999 
// if you ever need to add more ID's to your list, start at 1000000.
shuffle($list); //now it's randomly ordered

如果我理解正确,您正在尝试让用户从尚未注册用户的表中选择自己的唯一ID。如果是这种情况,那么我的用户表将包含userID和userName列。然后返回userName仍然为null的userid

$query = "SELECT userID, userName FROM User WHERE userName=''";
$result=mysql_query($query);
while ($row = mysql_fetch_assoc($result)) {
echo $row['userID'];
}
如果您想让他们自己选择,那么运行一个包含where子句的查询,该子句将检查用户输入的特定数字是否有名称。您可以使用$\u POST设置表单

<form method="POST" action="checkID.php">
<label for="userName">User Name:</label>
<input name="userName" maxlength="40" id="userName" type="text" />

<label for="userID">User ID:(int only)</label>
<input name="userID" maxlength="40" id="userID" type="text" />

<input type="submit">
</form>
然后是你的问题

$query = "SELECT userID, userName FROM User WHERE userName='' AND userID=$userID";
$result=mysql_query($query);

if ($result) {
echo "number is available";
$insert = "UPDATE User
SET userName=$userName
WHERE userID=$userID";

$inserted=mysql_query($insert);
if ($inserted) {
echo "you have been inserted as: ";
echo $userID;
echo $userName;;
}


}
我认为这是你的基础。显然,您需要运行一些验证和错误检查


希望这就是你想要的。如果没有,请告诉我,我会带其他东西回来。

好的,我会再试一次。你的目标是:

  • 混淆标识符的增长率-不连续、不可猜测、不可计算
  • 让它对智能手机用户仍然“可用”,因此长UUID或SHA1哈希超出范围
  • 避免对数据库进行随机猜测/暴力攻击
  • 使用外键仍具有良好的数据库性能
为了在数据库中保持良好的性能,您应该为PK保留一个标准的自动递增整数。请注意,InnoDB根据PK对行进行排序(请参见“”),因此,如果使用某种神奇的哈希作为PK,则会在聚集索引中进行大量重新排序,从而导致糟糕的写入性能

我建议使用加密算法(加密,而不是散列)对要混淆的ID进行加密和解密。此外,为了获得最佳混淆效果,您希望对结果字符串使用最小长度。生成的字符串应该仍然可用,因此您必须使用base64之类的东西来表示用户可读的字符串

请尝试以下加密示例:

function my_number_encrypt($data, $key, $base64_safe=true, $minlen=8) {
        $data = base_convert($data, 10, 36);
        $data = str_pad($data, $minlen, '0', STR_PAD_LEFT);
        $data = @mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $data, MCRYPT_MODE_CBC);
        if ($base64_safe) $data = str_replace('=', '', base64_encode($data));
        return $data;
}

function my_number_decrypt($data, $key, $base64_safe=true) {
        if ($base64_safe) $data = base64_decode($data.'==');
        $data = @mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $data, MCRYPT_MODE_CBC);
        $data = base_convert($data, 36, 10);
        return $data;
}

$key = "my super secret magic bytes";

$id = 12345678; // obtain_normal_key_from_mysql();

// give ID to user
$enc = my_number_encrypt($i, $key);

// get ID from user
$dec = my_number_decrypt($enc, $key);
// fetch from database using normal ID -> 12345678

// demo code
for($i=10000; $i<10050; $i++) {
        $enc = my_number_encrypt($i, $key);
        $dec = my_number_decrypt($enc, $key);
        echo "$i -> $enc -> $dec", PHP_EOL;
}


我认为,这篇文章可以进一步帮助你如果你使用patter([consennt][voice])*和小写字母,那么“人类可输入性”会增加。为什么不让用户选择自己的用户名,即你的ID,你在寻找什么?你的用户可能在智能手机上还有其他应用程序,你会麻烦他记住另一个id/密码。UUID可用于REST请求,因为您不想将主ID用于与用户客户端或合作伙伴的任何通信。此功能的一个扩展是使用S/KEY字典映射生成的ID,以使其真正易于键入(32位表示3个短单词)faqs.org/rfcs/rfc1760.html有dictionary.cool和simple解决方案,但该表不会被删除。两个或两个以上的insert请求同时发生了什么?如果你担心的话,交易就在你身边。交易与契约相结合。如果你有近十亿个ID,这就变得很困难了。例如,该文件的权重为GB。非常完整的答案“确定性”在这种情况下是不正确的:)这也是一种蛮力方法。。虽然是用SQL而不是PHP实现的,但这种方法似乎也会在找到一个数字之前强制执行一个解决方案。这不会很好地扩展;因此,我想我更喜欢@Mchl建议的种子表。如果列uid有索引,那么数据库就不会产生“暴力”,而是进行二进制搜索,并且扩展性更好。如果列uid具有“唯一约束”,则您只需获取错误并尝试下一个随机数。因此,外部while循环再次运行的可能性非常低。我喜欢此解决方案,但种子表的简单性吸引了我。我实际上不需要解密,因为我只将值存储在字段中。@Evert我只是想演示一种可扩展的方法。种子桌的简单性也吸引了我。但我不喜欢在达到上限时需要“重新播种”。加密解决方案是100%完全混淆,并且还隐藏了ID的范围。是的,但这是值得的。。我确实投票给你:)这是一个非常聪明的解决方案。+对于好的加密功能:)FWIW:也许使用Base32而不是Base64会更好。这消除了人工处理问题:不区分大小写,没有含糊不清的字符(0对O,1对l),没有特殊字符。。。但这会使绳子更长。
function my_number_encrypt($data, $key, $base64_safe=true, $minlen=8) {
        $data = base_convert($data, 10, 36);
        $data = str_pad($data, $minlen, '0', STR_PAD_LEFT);
        $data = @mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $data, MCRYPT_MODE_CBC);
        if ($base64_safe) $data = str_replace('=', '', base64_encode($data));
        return $data;
}

function my_number_decrypt($data, $key, $base64_safe=true) {
        if ($base64_safe) $data = base64_decode($data.'==');
        $data = @mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $data, MCRYPT_MODE_CBC);
        $data = base_convert($data, 36, 10);
        return $data;
}

$key = "my super secret magic bytes";

$id = 12345678; // obtain_normal_key_from_mysql();

// give ID to user
$enc = my_number_encrypt($i, $key);

// get ID from user
$dec = my_number_decrypt($enc, $key);
// fetch from database using normal ID -> 12345678

// demo code
for($i=10000; $i<10050; $i++) {
        $enc = my_number_encrypt($i, $key);
        $dec = my_number_decrypt($enc, $key);
        echo "$i -> $enc -> $dec", PHP_EOL;
}
10000 -> 1RXK468NYes -> 10000
10001 -> QdEov5mjMPA -> 10001
10002 -> 2gsgzWJgD+8 -> 10002
10003 -> 2zwPwhqr9HI -> 10003
10004 -> Xq+kDh1UFuM -> 10004
10005 -> wfwv6TrW9xY -> 10005
10006 -> 1Lck1L0HJ/U -> 10006
10007 -> v+3YY2zfL1A -> 10007
10008 -> 5AmGlqD8byM -> 10008
10009 -> pZBIpPnKXHU -> 10009
10010 -> CAeWdKGkk8c -> 10010
10011 -> fYddnLOSK6U -> 10011
10012 -> na8Ry0erHv8 -> 10012
10013 -> zxNj+ZJVMBY -> 10013
10014 -> gWJWC9VulZc -> 10014
10015 -> 5pR9B79eM/E -> 10015
10016 -> MQtpBhpzHRA -> 10016
10017 -> dW+3nejBEIg -> 10017
10018 -> znB/feM6104 -> 10018
10019 -> RtdRwwRyEcs -> 10019
10020 -> 4cW/OWT140E -> 10020
10021 -> dIvK9VjOevg -> 10021
10022 -> QxLdfrucc/Y -> 10022
10023 -> M0KN3sX10Gs -> 10023
10024 -> 827yFJyDCG4 -> 10024
10025 -> JF/VRj92qL8 -> 10025
10026 -> IXTvn/SCzek -> 10026
10027 -> L4nFwvhgwX8 -> 10027
10028 -> z0lve9nhgDA -> 10028
10029 -> m/UBgZzfIXo -> 10029
10030 -> IfWcrLKTHXk -> 10030
10031 -> n/jPFwKR/9A -> 10031
10032 -> j1mm2kbeWl0 -> 10032
10033 -> cm7mOQMVa6k -> 10033
10034 -> jCUuweEyRME -> 10034
10035 -> LDaMcOWKxjg -> 10035
10036 -> Zcrd5XzhhIk -> 10036
10037 -> j0Yg/fCjyAA -> 10037
10038 -> /LmlvRHmmmg -> 10038
10039 -> t0juuzGSKs4 -> 10039
10040 -> 9CoRCVXaak4 -> 10040
10041 -> tFmImR4j0JM -> 10041
10042 -> nI3Thy51hLg -> 10042
10043 -> mTCJh0/h2mE -> 10043
10044 -> S196xdyb3Os -> 10044
10045 -> ItOyUp+J4Q4 -> 10045
10046 -> DL87SidiOLM -> 10046
10047 -> d+Nw3xBqV44 -> 10047
10048 -> 3YzVelaC4uI -> 10048
10049 -> fAUJVOl6PaU -> 10049