Создайте случайный токен, проверьте дубликат - PullRequest
1 голос
/ 24 сентября 2010

У меня есть скрипт php, который создает случайный токен (A-Z, a-z, 0-9):

    function token($length) {

    $characters = array(
    "A","B","C","D","E","F","G","H","J","K","L","M",
    "N","P","Q","R","S","T","U","V","W","X","Y","Z",
    "a","b","c","d","e","f","g","h","i","j","k","m",
    "n","o","p","q","r","s","t","u","v","w","x","y","z",
    "1","2","3","4","5","6","7","8","9");

    //make an "empty container" or array for our keys
    $keys = array();

    //first count of $keys is empty so "1", remaining count is 1-6 = total 7 times
    while(count($keys) < $length) {
        //"0" because we use this to FIND ARRAY KEYS which has a 0 value
        //"-1" because were only concerned of number of keys which is 32 not 33
        //count($characters) = 33
        $x = mt_rand(0, count($characters)-1);
        if(!in_array($x, $keys)) {
           $keys[] = $x;
        }
    }

    foreach($keys as $key){
        $random .= $characters[$key];
    }

    return $random;

}

Работает отлично, но я хочу иметь возможность проверить определенную базу данных, чтобы убедиться, что этот токен никогда не использовался ранее. И если он есть, он мгновенно воссоздает новый токен перед выводом.

Я знаю, что могу использовать этот скрипт для проверки дубликата:

$check = mysqli_query($mysqli, "SELECT ".$table.".token FROM ".$table." WHERE ".$table.".token = '".$random."'");

        if(mysqli_num_rows($check) > 0){

        //ERROR: DUPLICATE TOKEN, CREATE A NEW TOKEN NOW...

        }

Мне просто нужна помощь, чтобы сложить все вместе, чтобы в случае обнаружения дубликата в базе данных он возвращался назад и пытался снова.

Ответы [ 7 ]

6 голосов
/ 24 сентября 2010

Используйте цикл do while:

do {
    $random = token(LENGTH);
} while (mysqli_num_rows(mysqli_query(YOUR_LOOKUP_QUERY)));

PS: Вы можете упростить свой код токена:

function token($length) {
    $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456789';
    return substr(str_shuffle($characters), 0, $length);
}
4 голосов
/ 24 сентября 2010

Поместите создание токена и проверьте в цикле do … while:

do {
    $random = token(10);
    $check = mysqli_query($mysqli, "SELECT ".$table.".token FROM ".$table." WHERE ".$table.".token = '".$random."'");
} while (mysqli_num_rows($check) > 0);

Кстати: Вы можете упростить свою функцию token следующим образом:

function token($length) {
    $characters = array(
        "A","B","C","D","E","F","G","H","J","K","L","M",
        "N","P","Q","R","S","T","U","V","W","X","Y","Z",
        "a","b","c","d","e","f","g","h","i","j","k","m",
        "n","o","p","q","r","s","t","u","v","w","x","y","z",
        "1","2","3","4","5","6","7","8","9");
    if ($length < 0 || $length > count($characters)) return null;
    shuffle($characters);
    return implode("", array_slice($characters, 0, $length));
}
3 голосов
/ 24 сентября 2010

Намного эффективнее настроить таблицу базы данных с использованием уникального ключа в токене - затем попробуйте вставить новое значение - если запрос успешно выполнен, вы получили новое значение, если он возвращает код ошибки 1022, ключ уже существует- если он возвращает другую ошибку - что-то еще пошло не так.

C.

1 голос
/ 24 сентября 2010

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

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

Примерно так генерируется 1000 «уникальных» токенов, но его можно легко настроить на цикл до тех пор, пока код не станет действительно уникальным, а не фиксированным числом циклов (и, возможно, этобыть немного подчищенным :)):

DECLARE @token as varchar(6),
        @numberOfLoops int,
        @currentLoop int,
        @pool varchar(50)       
-- Initialize 
SET @token = ''
SET @numberOfLoops = 1000
SET @currentLoop = 0
SET @pool = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890';

WHILE (@currentLoop < @numberOfLoops)
BEGIN
    SET @token = 
        SUBSTRING(@pool, CAST((RAND() * LEN(@pool) + 1) as int), 1) + 
        SUBSTRING(@pool, CAST((RAND() * LEN(@pool) + 1) as int), 1) + 
        SUBSTRING(@pool, CAST((RAND() * LEN(@pool) + 1) as int), 1) + 
        SUBSTRING(@pool, CAST((RAND() * LEN(@pool) + 1) as int), 1) + 
        SUBSTRING(@pool, CAST((RAND() * LEN(@pool) + 1) as int), 1) + 
        SUBSTRING(@pool, CAST((RAND() * LEN(@pool) + 1) as int), 1);
    print @token;


    SET @currentLoop = @currentLoop + 1;
END

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

0 голосов
/ 24 сентября 2010
 mysql> CREATE TABLE tokens (id integer primary key auto_increment, token varchar(36), UNIQUE(token));

<?php
  mysqli_query('INSERT INTO tokens (token) SELECT UUID();');
  $r = mysqli_query('SELECT token FROM tokens WHERE id = '.mysqli_insert_id());
  $result = mysqli_fetch_row($r);
  echo $result[0];
0 голосов
/ 24 сентября 2010
Цикл

A do-while был бы идеальным для этого, потому что вы хотите выполнить 'генерацию нового токена', когда сгенерированный токен не уникален.

do {
    $token = token(...);
} while (token_exists($token));

Asвы сделали с вашей функцией token, вы должны поместить код поиска в базу данных в новую функцию, чтобы основной алгоритм (продолжал генерировать новый токен, пока он не был уникальным) был четко сформулирован.В приведенном выше коде я предполагал, что функция будет называться token_exists и будет выглядеть примерно так:

function token_exists($random) {
    $check = mysqli_query($mysqli, "SELECT ".$table.".token FROM ".$table." WHERE ".$table.".token = '".$random."'");
    return (mysqli_num_rows($check) > 0);
}
0 голосов
/ 24 сентября 2010

Вы можете заключить весь код в цикл while, который будет повторять код до тех пор, пока вы не найдете правильный код (не повторяется).Например;

do { 
  $nextToken = token(32);
  $check = mysqli_query($mysqli, "SELECT ".$table.".token FROM ".$table." WHERE ".$table.".token = '".$nextToken."'");
} while ( mysql_num_rows($check) > 0 );

Это будет многократно загружать случайный токен, пока вы не найдете токен, который не генерирует никаких строк из функции mysql_num_rows (т.е. ранее не повторялся).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...