Рецепты программирования на PHP или практические советы по программированию → Безопасная регистрация в один шаг и 2 поля
Много потенциальных клиентов теряется из-за неправильно организованной регистрации. Посетитель не хочет заполнять длинные анкеты, что-то подтверждать, кликать много ссылок, делать много шагов. Короче, он не хочет напрягаться. С каждым шагом клиента - вероятность достижения цели будет значительно снижаться.
Наша задача - облегчить процесс регистрации со стороны клиента, т.е. сделать регистрацию в один шаг. При этом, мы не хотим потерять безопасность приложения, т.е. требуется подтверждение регистрации. Итак, расскажу общую идею регистрации с подтверждением в один шаг.
1. В форме авторизации делаем всего 2 поля: e-mail и код подтверждения. Пароль мы будем генерить самостоятельно, не напрягая клиента. Он его потом сможет изменить. В качестве логина мы будем использовать его электронный адрес. Все другие данные (ник, имя, аватар, что-то-еще-чего-вам-необходимо) можно вынести в отдельную анкету - личный кабинет. Итак, клиент заполняет только свой е-мейл.
2. При выходе из поля e-mail делаем на ajax следующие проверки:
- корректен ли адрес (регулярное выражение);
- если ли такой адрес уже среди зарегистрированных пользователей. Тут, в зависимости от вашего алгоритма можно либо напомнить ему пароль, либо выдать сообщение;
- на сервере заносим клиента в БД, генерим ему короткий код, отправляем этот код на его е-мейл и подсвечиваем фокусом поле код в форме, выдав надпись типа "введите код, полученный в письме".
3. Он вводит код, мы сравниваем его с кодом сгенерированным автоматически, и если совпадает - меняем статус клиенту на подтвержденный, генерим пароль и хеш и авторизуем его на сайте.
Вот и все. Для клиента этот процесс занимает минуту-две и не пугает количеством полей.
Итак, начнем. Все скрипты я максимально упростил заведомо, чтобы показать суть процесса.
Форма ввода:
| 1 | <form method="POST" action=""> |
| 2 | E-mail: <input type="text" id="email" name="email" /><br /> |
| 3 | Код (высылается на e-mail): <input type="text" id="key" name="key" /><br /> |
| 4 | <input type="submit" value="ok" id="submit" /> |
| 5 | </form> |
JavaScript, обрабатывающий форму и передающий результаты на сервер.
Для простоты понимания и чтобы не писать много лишних функций (например, ajax), мы будем использовать библиотеку jquery. Ниже код. Его помещаем либо в html, где форма, либо отдельным файлом js.
| 1 | //подключаем библиотеку jquery (можно скопировать локально к себе на сайт |
| 2 | <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script> |
| 3 | |
| 4 | //ниже скрипты, которые начнут отрабатываться после загрузки страницы (аналог onload) |
| 5 | |
| 6 | <script type="text/javascript"> |
| 7 | $().ready(function() { |
| 8 | |
| 9 | //это регулярное выражение проверки корректности адреса e-mail |
| 10 | var re = /^([a-z0-9_-]+.)*[a-z0-9_-]+@([a-z0-9][a-z0-9-]*[a-z0-9].)+[a-z]{2,4}$/i; |
| 11 | |
| 12 | //клиент вышел из поля email |
| 13 | $('#useremail').blur(function() { |
| 14 | |
| 15 | //проверяем, что он ввел |
| 16 | if (this.value == '' || !re.test(this.value)) { alert('Некорректный e-mail'); return false; } |
| 17 | |
| 18 | //передаем скрипту на сервере e-mail посетителя |
| 19 | $.get('register.php?email='+email,function(data) { |
| 20 | //в случае успеха, скрипт возвратит надпись 'ok', иначе - сообщение об ошибке |
| 21 | if(data != 'ok') { alert('Некорректный e-mail'); return false; } |
| 22 | }); |
| 23 | }); |
| 24 | |
| 25 | //клиент нажал ok на форме |
| 26 | $('#submit').click(function() { |
| 27 | //проверяем, что он ввел |
| 28 | var email = $('#email').val(); |
| 29 | var key = $('#key').val(); |
| 30 | if (email == '' || !re.test(email)) { alert('Некорректный e-mail'); return false; } |
| 31 | //проверяем код (пусть, код у нас обязательно содержит 4 цифры и ничего более) |
| 32 | if (!/^d{4}$/.test(key)) { alert('Некорректный код подтверждения'); return false; } |
| 33 | |
| 34 | //все проверки сделаны, пробуем зарегистрировать клиента |
| 35 | //передаем скрипту на сервере e-mail посетителя и код |
| 36 | $.get('register.php?email='+email+'&key='+key,function(data) { |
| 37 | //в случае успеха, скрипт возвратит надпись 'ok', иначе - сообщение об ошибке |
| 38 | if(data != 'ok') { alert(data); return false; } |
| 39 | |
| 40 | //иначе полагаем, что серверный скрипт залогинил посетителя и просто перегружаем страницу |
| 41 | else document.refresh; |
| 42 | |
| 43 | }); |
| 44 | }); |
| 45 | }); |
| 46 | </script> |
База данных mySQL
Создаем простую табличку посетителей (остальные поля можно добавлять по желанию).
| 1 | CREATE TABLE `users` ( |
| 2 | `id` int(11) NOT NULL auto_increment, |
| 3 | `email` varchar(255) default NULL, |
| 4 | `password` varchar(50) default NULL, |
| 5 | `hash` varchar(255) default NULL, |
| 6 | `confirmed` enum('yes','no') NOT NULL default 'no', |
| 7 | PRIMARY KEY (`id`), |
| 8 | UNIQUE KEY `email` (`email`), |
| 9 | KEY `confirmed` (`confirmed`), |
| 10 | KEY `hash` (`hash`), |
| 11 | ) ENGINE=MyISAM DEFAULT CHARSET=cp1251 AUTO_INCREMENT=1; |
Серверный скрипт (кому передает данные JavaScript)
Несмотря на то, что JavaScript проверил на корректность все поля, в серверном сценарии необходимо повторить проверки во избежании всяких казусов
| 1 | <?php |
| 2 | //считываем и проверяем значения e-mail и ключа |
| 3 | $email = isset($_GET["email"]) && preg_match("/^([a-z0-9_\-]+\.)*[a-z0-9_\-]+@([a-z0-9][a-z0-9\-]*[a-z0-9]\.)+[a-z]{2,4}$/i",$_GET["email"]) ? $_GET["email"] : FALSE; |
| 4 | $key = isset($_GET["key"]) && preg_match("/^\d{4}$",$_GET["key"]) ? $_GET["key"] : FALSE; |
| 5 | |
| 6 | $response = FALSE; //это ответ, который выдаст этот скрипт |
| 7 | if($email) { |
| 8 | //соединяемся с БД |
| 9 | $conn = new mysqli(HOST,USER,PASSWORD,DB); |
| 10 | //проверяем, если ли в нашей табличке такой неподтвержденный посетитель |
| 11 | $q = "SELECT * FROM users WHERE email='".$email; |
| 12 | $result = $conn->query($q) or die("Ошибка БД"); |
| 13 | //вытаскиваем первую запись (она будет всего одна в лучшем случае, т.к. поле email - уникально |
| 14 | $r = $result->fetch_array(MYSQLI_ASSOC); |
| 15 | if($r && $key) { //неподтвержденный посетитель найден и он задал код |
| 16 | if($r["confirmed"] === 'yes') die("Вы уже зарегистрированы"); |
| 17 | //проверяем корректность кода |
| 18 | if($r["password"] === $key) { |
| 19 | //код верный. |
| 20 | //Меняем статус посетителя на подтвержденный, меняем пароль, генерим хеш |
| 21 | //для записи в cookies для последующего автологина на сайт |
| 22 | //тут можно писать что угодно, лишь бы случайно. Я беру время и email для начальной генерации |
| 23 | $password = substring(hash('MD5',time().$email),1,5); |
| 24 | $hash = hash('MD5',$password.rand(time())); |
| 25 | $conn->query("UPDATE users SET confirmed='yes',password='".$password."',hash='".$hash."' WHERE id=".$r["id"]); |
| 26 | |
| 27 | //логиним посетителя через сессию |
| 28 | ini_get('session.auto_start') or session_start(); |
| 29 | $_SESSION["logged"] = 1; |
| 30 | |
| 31 | //пишем куки на месяц |
| 32 | setcookie("hash",$hash,time() + 2592000,"/"); |
| 33 | |
| 34 | //отправляем письмо посетителю об успешной регистрации |
| 35 | $mailHeaders = "From: domain.ru<admin@domain.ru>\r\nReply-To: domain.ru<<admin@domain.ru>>\r\nContent-type: text/html; charset=windows-1251 \r\n"; |
| 36 | $subj = "Регистрация на сайте domain.ru"; |
| 37 | $msg = "Поздравляем! Ваш пароль: ".$password; |
| 38 | @mail($email,$subj,$msg,$mailHeaders); |
| 39 | |
| 40 | //статус - готово |
| 41 | die("ok"); |
| 42 | |
| 43 | } else die("Неверный код подтверждения"); |
| 44 | } elseif(!$key && $r) $response = "Задайте код подтверждения"; |
| 45 | |
| 46 | //если не задан код, и посетитель не найден, делаем предварительную регистрацию |
| 47 | //это случается при выходе из поля email |
| 48 | elseif(!$key && !$r) { |
| 49 | $password = rand(1000,9999); //генерим 4-х значный цифровой ключ |
| 50 | //вводим посетителя |
| 51 | $conn->query("INSERT INTO users (password,email) VALUES ('".$password."',email='".$email."')"; |
| 52 | //статус - готово |
| 53 | die("ok"); |
| 54 | } |
| 55 | } else die("Вы не ввели e-mail или ввели его некорректно"); |
На этом регистрацию можно считать завершенной. Функции логина написать довольно просто:
| 1 | <?php |
| 2 | //не забываем стартовать сессию, только один разок! |
| 3 | ini_get('session.auto_start') or session_start(); |
| 4 | |
| 5 | function isLogged() { |
| 6 | if(isset($_SESSION["logged"]) && $_SESSION["logged"] == 1) return TRUE; |
| 7 | else { |
| 8 | //побуем залогинить посетителя через куки |
| 9 | if(isset($_COOKIES["hash"]) && preg_match("/^[a-z0-9]{20,100}$/",$_COOKIES["hash"])) { |
| 10 | $q = "SELECT * FROM users WHERE hash='".$_COOKIES["hash"]."' AND confirmed='yes'"; |
| 11 | $result = $conn->query($q) or die("Ошибка БД"); |
| 12 | if($user = $result->fetch_array(MYSQLI_ASSOC)) { |
| 13 | //пользователь найден, авторизуем через сессию |
| 14 | $_SESSION["logged"] = 1; |
| 15 | //здесь можно сгенерить новый хеш, можно сделать еще чего-то |
| 16 | return TRUE; |
| 17 | } |
| 18 | } |
| 19 | //стираем на всяк случай сессию и куки |
| 20 | session_unset(); |
| 21 | $_SESSION = array(); |
| 22 | session_destroy(); |
| 23 | setcookie("hash","",time() - 3600,"/"); |
| 24 | return FALSE; |
| 25 | } |
Ну и напоследок - использование функции авторизации с формой.
| 1 | <?php |
| 2 | if(isLogged()) echo "Привет, авторизованный друг"; |
| 3 | else echo login(); |
| 4 | |
| 5 | function login() { |
| 6 | //форма засабмичена, проверяем пользователя |
| 7 | if(isset($_REQUEST["submit"]) && $_REQUEST["submit"]) { |
| 8 | $email = $_POST["email"] && preg_match("/^([a-z0-9_\-]+\.)*[a-z0-9_\-]+@([a-z0-9][a-z0-9\-]*[a-z0-9]\.)+[a-z]{2,4}$/i",$_POST["email"]) ? $_POST["email"] : FALSE; |
| 9 | $password = $_POST["password"] && preg_match( ... регулярное выражение на допустимые пароли) ? $_POST["password"] : FALSE; |
| 10 | if($email && $password) { |
| 11 | $q = "SELECT * FROM users WHERE email='".$email."' AND password='".$pasword."' AND confirmed='yes'"; |
| 12 | $result = $conn->query($q); |
| 13 | $r = $result->fetch_array(MYSQLI_ASSOC); |
| 14 | if($r) { |
| 15 | //пользователь найден, авторизуем через сессию |
| 16 | $_SESSION["logged"] = 1; |
| 17 | //пишем куки на месяц (если надо) |
| 18 | if($r["hash"]) setcookie("hash",$r["hash"],time() + 2592000,"/"); |
| 19 | //вуаля |
| 20 | return "Привет, мы тебя авторизовали!"; |
| 21 | } |
| 22 | } |
| 23 | return "Нет, нет, мы не можем тебя авторизовать!"; |
| 24 | } else { //показываем форму |
| 25 | return |
| 26 | <form method="POST" action=""> |
| 27 | E-mail: <input type="text" name="email" /><br /> |
| 28 | Пароль: <input type="text" name="password" /><br /> |
| 29 | <input type="submit" value="ok" id="submit" /> |
| 30 | </form>; |
| 31 | |
| 32 | } |
| 33 | } |
Теперь, все легко. Чтобы создать личный кабинет, достаточно использовать всю ту же функцию.
| 1 | <?php |
| 2 | if(isLogged()) { |
| 3 | //показываем личный кабинет |
| 4 | } else echo login(); |
Да, еще при использовании AJAX не забываем корректно конвертировать кодировку, т.к. в методе GET может передаваться не только CP1251, но и UTF8. Подробнее - тут.
О да, я справился с этим длинным постом. Не гарантирую, что copy-paste из этого поста сразу у вас заработает (я выкладывал не целиком код, а сами идеи), но если вы программист - то все поймете, это совсем не сложно. Вот, кстати, пример, как это работает в жизни.

25.08.2011 → @ROMANIJA@/COM.RU написал:
КЛАС