Простая система регистрации на PHP и MySql
Для создания системы регистрации необходимо проделать много работы. Вы должны написать код для проверки email адресов, рассылки писем для подтверждения, восстановления пароля, валидации входных данных и т.д. Даже когда вы все это проделаете, посетители не хотят регистрироваться, потому что это требует от них много усилий.
В этом уроке мы сделаем очень простую систему регистрации, которой даже не потребуется хранить пароль! Её очень просто модифицировать и встроить в существующий сайт на PHP.
Идея
Как система регистрации будет работать:
- Форма для логина и регистрации объединены в одну, где пользователи будут указывать свой email;
- Если указанного почтового адреса нет в базе, создается новая запись. Генерируется случайный ключ и отправляется на почту пользователя в виде ссылки, которая активна в течении 10 минут;
- При нажатии на ссылку пользователь переходит на ваш сайт. Система сама авторизует пользователя по переданному в ссылке ключу.
Преимущества такого подхода:
- Нет необходимости в хранении и валидации паролей;
- Нет необходимости в системе восстановления паролей;
- Процесс регистрации очень прост и привлекателен.
Недостатки:
- Система безопасна до те пор, пока в безопасности почта пользователя. Если у кого-то есть доступ к почте пользователя, он может легко авторизоваться. Но это также относится и к другим системам регистрации (с функционалом восстановления пароля);
- Email передается открыто и может быть перехвачен (также относится ко всем системам регистрации, не использующим HTTPS);
- Если не уделить должного внимания к настройке и оформлению исходящих писем, есть шанс, что они попадут в спам.
Исходя из перечисленного, наша система регистрации очень удобна в использовании, но не полностью безопасна, поэтому использовать её лучше для простых вещей, вроде регистрации на форуме и сервисов, которые не используют важную конфиденциальную информацию.
Использование системы регистрации
Если вы просто хотите начать использовать эту систему, не просматривая урок, вам необходимо:
- Скачайте исходники по ссылке выше;
- В zip файле найдите файл tables.sql. Импортируйте его в свою базу с помощью phpMyAdmin. Также можно просто скопировать SQL код из файла и выполнить его;
- Откройте includes/main.php и введите данные для соединения с базой имя пользователя/пароль. В этом же файле надо указать email, который будет использован в качестве отправного. Некоторые почтовые системы блокируют письма без указания это адреса;
- Загрузите файлы на сервер;
- Добавьте этот код на каждую страницу, если хотите, что бы она была доступна только авторизованным пользователям;
Если хотите знать как работает система регистрации, продолжайте читать! 🙂
HTML
Как всегда, начнем с HTML разметки формы для регистрации. Следующий код расположен в файле index.php. Этот файл так же содержит PHP код, который обрабатывает входные данные и другие полезные функции системы. Об этом поговорим ниже.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Tutorial: Super Simple Registration System With PHP & MySQL</title>
<!-- The main CSS file -->
<link href="assets/css/style.css" rel="stylesheet" />
<!--[if lt IE 9]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<form id="login-register" method="post" action="index.php">
<h1>Login or Register</h1>
<input type="text" placeholder="your@email.com" name="email" autofocus />
<p>Enter your email address above and we will send <br />you a login link.</p>
<button type="submit">Login / Register</button>
<span></span>
</form>
<!-- JavaScript Includes -->
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="assets/js/script.js"></script>
</body>
</html>
В теге <head> подключаем основной CSS файл (в уроке он не представлен, посмотрите его в исходниках). Перед закрытием <body> подключаем jQuery и файл script.js, который напишем ниже.
JavaScript
jQuery следит за событием submit формы, вызывает e.preventDefault() предотвращая отправку формы и посылает AJAX вместо этого. В зависимости от ответа сервера будет показано сообщение.
$(function(){
var form = $('#login-register');
form.on('submit', function(e){
if(form.is('.loading, .loggedIn')){
return false;
}
var email = form.find('input').val(),
messageHolder = form.find('span');
e.preventDefault();
$.post(this.action, {email: email}, function(m){
if(m.error){
form.addClass('error');
messageHolder.text(m.message);
}
else{
form.removeClass('error').addClass('loggedIn');
messageHolder.text(m.message);
}
});
});
$(document).ajaxStart(function(){
form.addClass('loading');
});
$(document).ajaxComplete(function(){
form.removeClass('loading');
});
});
CSS класс .loading добавляется к форме, пока отправляется AJAX запрос (это происходит в функциях ajaxStart() и ajaxComplete()). Этот класс добавляет вращающийся gif индикатор, а также блокирует повторный сабмит формы. CSS класс .loggedIn cдобавляется email был отправлен и блокирует форму перманентно.
Схема базы данных
Наша простая система регистрации использует две MySql таблицы (SQL код для их создания вы найдете в файле tables.sql). Первая таблица содержит данные пользователей, вторая — попытки залогиниться.
Таблица reg_users:
id - integer email - varchar rank - varchar (administrator|regular) registered - timestamp last_login - timestamp token - varchar token_validity - timestamp
Система не использует пароль, что видно по отсутствию соответствующего поля в таблице. Есть поле token и token_validity. token — создается когда пользователь входит в систему и отправляется ему на почту. token_validity — таймстамп, задающий время действия пароля (10 минут).
Каждый раз, когда кто-то пытается войти в систему, во второй таблице создается запись. Это необходимо для ограничения попыток логина, 10 попыток каждые 10 минут и 20 каждый час. Если ограничения нарушаются, то ip адрес блокируется.
Таблица reg_login_attempt:
id - integer ip - integer email - varchar ts - timestamp
ip в базе хранится как число, полученное с помощью PHP функции ip2long.
PHP
Теперь займемся PHP кодом. Основной функционал системы реализован в классе User. Этот класс использует ORM библиотеку idiorm, для связи с базой данных (мы также использовали её в уроке Создание графиков с помощью jQuery и xCharts). Класс User обрабатывает данные из базы данных, генерирует коды и проверяет их. Класс имеет простой интерфейс, который легко встроить в существующий PHP сайт.
class User{
// Private ORM instance
private $orm;
/**
* Находим юзера по коду
* @param string $token
* @return User
*/
public static function findByToken($token){
// находим код и убеждаемся, что он действителен
$result = ORM::for_table('reg_users')
->where('token', $token)
->where_raw('token_validity > NOW()')
->find_one();
if(!$result){
return false;
}
return new User($result);
}
/**
* Авторизация или регистрация юзера
* @param string $email
* @return User
*/
public static function loginOrRegister($email){
// Если есть такой юзер - возвращаем
if(User::exists($email)){
return new User($email);
}
// иначе - создаем и возвращаем
return User::create($email);
}
/**
* Создание нового юзера
* @param string $email
* @return User
*/
private static function create($email){
// Записываем и возвращаем
$result = ORM::for_table('reg_users')->create();
$result->email = $email;
$result->save();
return new User($result);
}
/**
* Проверка существования пользователя.
* @param string $email
* @return boolean
*/
public static function exists($email){
$result = ORM::for_table('reg_users')
->where('email', $email)
->count();
return $result == 1;
}
/**
* Конструктор
* @param $param ORM instance, id, email или null
* @return User
*/
public function __construct($param = null){
if($param instanceof ORM){
// передали ORM instance
$this->orm = $param;
}
else if(is_string($param)){
// или email
$this->orm = ORM::for_table('reg_users')
->where('email', $param)
->find_one();
}
else{
$id = 0;
if(is_numeric($param)){
// или id
$id = $param;
}
else if(isset($_SESSION['loginid'])){
// id не передали, ищем в сессии
$id = $_SESSION['loginid'];
}
$this->orm = ORM::for_table('reg_users')
->where('id', $id)
->find_one();
}
}
/**
* Генерация SHA1 ключей.
* @return string
*/
public function generateToken(){
$token = sha1($this->email.time().rand(0, 1000000));
// Сохраняем в базу,
// и делаем активной в течение 10 минут
$this->orm->set('token', $token);
$this->orm->set_expr('token_validity', "ADDTIME(NOW(),'0:10')");
$this->orm->save();
return $token;
}
/**
* Авторизация
* @return void
*/
public function login(){
// Запоминаем в сессии
$_SESSION['loginid'] = $this->orm->id;
// Обновим last_login
$this->orm->set_expr('last_login', 'NOW()');
$this->orm->save();
}
/**
* Выход пользователя.
* @return void
*/
public function logout(){
$_SESSION = array();
unset($_SESSION);
}
/**
* Проверка авторизации.
* @return boolean
*/
public function loggedIn(){
return isset($this->orm->id) && $_SESSION['loginid'] == $this->orm->id;
}
/**
* Администратор?
* @return boolean
*/
public function isAdmin(){
return $this->rank() == 'administrator';
}
/**
* Роль пользователя: admin или regular.
* @return string
*/
public function rank(){
if($this->orm->rank == 1){
return 'administrator';
}
return 'regular';
}
/**
* Доступ к полям
* @param string $key
* @return mixed
*/
public function __get($key){
if(isset($this->orm->$key)){
return $this->orm->$key;
}
return null;
}
}
Коды генерируются с помощью алгоритма SHA1 и сохраняются в базе. Для установления действительности кодов мы используем функции MySql.
Мы используем метод __get для обращения к полям из базы данных, как к свойствам класса, например: $user->email, $user->token. Пример того как все это использовать — дальше.
В файле functions.php находятся вспомогательные функции.
function send_email($from, $to, $subject, $message){
// Отправка писем
$headers = 'MIME-Version: 1.0' . "\r\n";
$headers .= 'Content-type: text/plain; charset=utf-8' . "\r\n";
$headers .= 'From: '.$from . "\r\n";
return mail($to, $subject, $message, $headers);
}
function get_page_url(){
// Формирует URL
$url = 'http'.(empty($_SERVER['HTTPS'])?'':'s').'://'.$_SERVER['SERVER_NAME'];
if(isset($_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_URI'] != ''){
$url.= $_SERVER['REQUEST_URI'];
}
else{
$url.= $_SERVER['PATH_INFO'];
}
return $url;
}
function rate_limit($ip, $limit_hour = 20, $limit_10_min = 10){
// Количество попыток логина с ip за час
$count_hour = ORM::for_table('reg_login_attempt')
->where('ip', sprintf("%u", ip2long($ip)))
->where_raw("ts > SUBTIME(NOW(),'1:00')")
->count();
// за 10 минут
$count_10_min = ORM::for_table('reg_login_attempt')
->where('ip', sprintf("%u", ip2long($ip)))
->where_raw("ts > SUBTIME(NOW(),'0:10')")
->count();
if($count_hour > $limit_hour || $count_10_min > $limit_10_min){
throw new Exception('Too many login attempts!');
}
}
function rate_limit_tick($ip, $email){
// Создание записи в таблице reg_login_attempt
$login_attempt = ORM::for_table('reg_login_attempt')->create();
$login_attempt->email = $email;
$login_attempt->ip = sprintf("%u", ip2long($ip));
$login_attempt->save();
}
function redirect($url){
header("Location: $url");
exit;
}
С помощью функций rate_limit и rate_limit_tick отслеживается количество попыток логина. При каждой попытке залогиниться в таблице reg_login_attempt появляется новая запись. Эти функции вызываются при сабмите формы для логина.
Код ниже обрабатывает входные данные и возвращает JSON ответ, который обрабатывается в файле assets/js/script.js.
try{
if(!empty($_POST) && isset($_SERVER['HTTP_X_REQUESTED_WITH'])){
// вывод JSON заголовка
header('Content-type: application/json');
// проверка правильности email адреса
if(!isset($_POST['email']) || !filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)){
throw new Exception('Please enter a valid email.');
}
// Проверка количества попыток:
rate_limit($_SERVER['REMOTE_ADDR']);
// Запись попытки
rate_limit_tick($_SERVER['REMOTE_ADDR'], $_POST['email']);
// Пишем письмо
$message = '';
$email = $_POST['email'];
$subject = 'Your Login Link';
if(!User::exists($email)){
$subject = "Thank You For Registering!";
$message = "Thank you for registering at our site!\n\n";
}
// Авторизация или регистрация
$user = User::loginOrRegister($_POST['email']);
$message.= "You can login from this URL:\n";
$message.= get_page_url()."?tkn=".$user->generateToken()."\n\n";
$message.= "The link is going expire automatically after 10 minutes.";
$result = send_email($fromEmail, $_POST['email'], $subject, $message);
if(!$result){
throw new Exception("There was an error sending your email. Please try again.");
}
die(json_encode(array(
'message' => 'Thank you! We\'ve sent a link to your inbox. Check your spam folder as well.'
)));
}
}
catch(Exception $e){
die(json_encode(array(
'error'=>1,
'message' => $e->getMessage()
)));
}
При успешной авторизации или регистрации код вышлет письмо со ссылкой для входа. Код для входа будет доступен в глобальном массиве $_GET, т.к. мы передаем его в ссылке.
При переходе по ссылке будет запущен этот код:
if(isset($_GET['tkn'])){
// Проверка действительности кода
$user = User::findByToken($_GET['tkn']);
if($user){
// Код действителен, перенаправляем на закрытую страницу.
$user->login();
redirect('protected.php');
}
// Код недействителен.
redirect('index.php');
}
Функция $user->login() инициализирует сессию это значит что при посещении любой страницы, мы будем знать, что пользователь авторизован.
Выход из системы:
if(isset($_GET['logout'])){
$user = new User();
if($user->loggedIn()){
$user->logout();
}
redirect('index.php');
}
Если пользователь уже вошел, ему не надо видеть форму для логина. Здесь нам пригодится метод $user->loggedIn():
$user = new User();
if($user->loggedIn()){
redirect('protected.php');
}
Наконец, код, чтобы сделать любую страницу доступно только после авторизации:
// Для защиты страниц вашего сайта, подключите main.php
// и создайте новый объект типа User.
require_once 'includes/main.php';
$user = new User();
if(!$user->loggedIn()){
redirect('index.php');
}
После этой проверки вы можете быть уверенным, что пользователь успешно вошел в систему. Вы также можете получить доступ к информации из базы данных. Для вывода почты роли пользователя используйте код:
echo 'Your email: '.$user->email; echo 'Your rank: '.$user->rank();
rank() — функция, потому что роли может быть две. В базе они хранятся как 0 или 1, поэтому перед выводом надо конвертировать значение из базы.
Наша система регистрации готова!
Смотрите также: система регистрации пользователей на PHP, MySql и jQuery, Регистрация в один клик от Google.






Не плохо!
А видео урок есть по данной теме?