Уважаемые друзья, временно не работает форма вопрос - ответ. В бижайшее время починю. Если у кого есть вопросы, можете присылать мне на почту bezramok-tlt@mail.ru

Восстановлени пароля пользователя php + mysql

Привет друзья. В самом начале создания своего блога я написал статью как создать регистрацию и авторизацию на PHP + MySQL. Данная статья стала очень актуальной на моем блоге и требует своего логического продолжения, а именно это восстановление пароля пользователя. Ища информацию на просторах Рунета вы много найдете статей как создать регистрацию и авторизацию пользователей на сайте, но при этом все опускают тот момент, когда пользователь забыл свой пароль. В данной статье мы исправим этот досадный момент.

Восстановление пароля пользователя может быть двух способов:

  • Когда пароль в базе данных хранится не в зашифрованном виде, то вы можете выслать данный пароль пользователю на его email. (Хранить пароли в открытом виде очень небезопасно и так делать нельзя!).
  • Второй вариант, когда пароль хранится в зашифрованном виде, например, по средствам md5() и возможность дешифровать его нет. Тогда нужно создавать новый пароль и обновлять запись в таблице.

Рассмотрим структуру нашей таблицы:

Таблица bez_reg

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

Сейчас попробуем описать алгоритм действий для реализации восстановления пароля пользователя на php + mysql.

  • Показываем форму с вводом логина для восстановления.
  • Если такой логин есть, то шлем ему письмо ссылкой и ключом в качестве ключа используем строку из ячейки active_hex.
  • Когда пользователь прошел по ссылке проверяем ключ, если ключ совпадает с полем active_hex то выводим форму с двумя полями для вода нового пароля. Как только пользователь сменил пароль, в целях безопасности генерируем новый active_hex, чтобы никто не смог воспользоваться ссылкой восстановления пароля повторно! Далее отправляем письмо о том, что пароль сменен и ссылку для входа.
  • Пользователь входит с новым паролем.

Я не буду здесь расписывать все скрипты, вы можете ознакомится с ними в первой статье регистрация и авторизация на PHP + MySQL. Здесь я выложу дополнения к данным скриптам.

В каталоге scripts создаем новую папку reminder. В данном каталоге создаем три файла:

  • form_reminder.html
  • new_pass_form.html
  • reminder.php

Структура каталогов регистрация и авторизация php + mysql

Далее переходим в index.php и подключаем наши файлы в switch();

Файл: index.php

<?php
	/**
	* Главный файл (переключатель)
	* Site: http://bezramok-tlt.ru
	* Регистрация пользователя письмом
	*/

	//Запускаем сессию
	session_start();

	//Устанавливаем кодировку и вывод всех ошибок
	header('Content-Type: text/html; charset=UTF8');
	error_reporting(E_ALL);

	//Включаем буферизацию содержимого
	ob_start();

	//Определяем переменную для переключателя
	$mode = isset($_GET['mode'])  ? $_GET['mode'] : false;
	$user = isset($_SESSION['user']) ? $_SESSION['user'] : false;
	$err = array();


	//Устанавливаем ключ защиты
	define('BEZ_KEY', true);
	 
	//Подключаем конфигурационный файл
	include './config.php';
	 
	//Подключаем скрипт с функциями
	include './func/funct.php';

	//подключаем MySQL
	include './bd/bd.php';

	switch($mode)
	{
		//Подключаем обработчик с формой регистрации
		case 'reg':
			include './scripts/reg/reg.php';
			include './scripts/reg/reg_form.html';
		break;
		
		//Подключаем обработчик с формой авторизации
		case 'auth':
			include './scripts/auth/auth.php';
			include './scripts/auth/auth_form.html';
			include './scripts/auth/show.php';
		break;

		//Восстановление пароля
		case 'reminder';
			include './scripts/reminder/reminder.php';
			//Выводим нужную форму для пользователя
			//Проверяем что ключ не проходит по ссылке после востановления
			if(isset($_GET['key']))
			{
				//Проверяем есть ли такой ключ
				$sql = 'SELECT COUNT(*) AS `total`
						FROM `'. BEZ_DBPREFIX .'reg`
						WHERE `active_hex` = :active_hex';

				//Подготавливаем PDO выражение для SQL запроса
				$stmt = $db->prepare($sql);
				$stmt->bindValue(':active_hex', $_GET['key'], PDO::PARAM_STR);
				if($stmt->execute())
				{
					$row = $stmt->fetch(PDO::FETCH_ASSOC);

					//Проверяем что вернул запрос
					if($row['total'] == 0)
						include './scripts/reminder/form_reminder.html';
					else
					{
						//Отправляем на главную страницу
						//header('Location: '. BEZ_HOST);
						//exit;
						include './scripts/reminder/new_pass_form.html';
					}
				}
			}
			else
			{
				if(!isset($_GET['newpass']))
					include './scripts/reminder/form_reminder.html';
			//	include './scripts/reminder/new_pass_form.html';
			}
		break;
    
	}
    
	//Получаем данные с буфера
	$content = ob_get_contents();
	ob_end_clean();

	//Подключаем наш шаблон
	include './html/index.html';
?>			

Создаем html разметку для формы восстановления пароля пользователя в файле form_reminder.html

Файл: form_reminder.html

<div class="col-md-8 col-md-offset-2"> 
<div class="panel panel-primary">
  <div class="panel-heading"><h4>Восстановление параметров авторизации</h4></div>
  <div class="panel-body">
    <p>Если забыли свои параметры авторизации, не переживайте, их можно восстановить. Для этого введите, пожалуйста, ниже свой email.</p>
    <form class="form-horizontal" action="" method="POST">
  <div class="form-group">
    <label for="inputEmail" class="col-xs-2 control-label">Адрес email:</label>
    <div class="col-xs-10">
     <input type="email" class="form-control" id="inputEmail" name="email" placeholder="Введите email">
    </div>
  </div>
 <div class="form-group">
    <div class="col-xs-offset-2 col-xs-10">
      <button type="submit" class="btn btn-primary" name="reminder">Восстановить</button>
   </div>
  </div>
</form>
  </div>
</div>
</div>

Далее создаем html разметку для ввода нового пароля пользователя в файле new_pass_form.html

Файл: new_pass_form.html

<div class="col-md-8 col-md-offset-2"> 
<div class="panel panel-primary">
  <div class="panel-heading"><h4>Восстановление пароля</h4></div>
  <div class="panel-body">
    <form class="form-horizontal" action="" method="POST">
  <div class="form-group">
    <label for="inputPass" class="col-xs-2 control-label">Пароль:</label>
    <div class="col-xs-10">
     <input type="password" class="form-control" id="inputPass" name="pass" placeholder="Введите пароль">
    </div>
  </div>
    <div class="form-group">
    <label for="inputPass2" class="col-xs-2 control-label">Повторите пароль:</label>
    <div class="col-xs-10">
     <input type="password" class="form-control" id="inputPass2" name="pass2" placeholder="Введите подтверждение пароля">
    </div>
  </div>
 <div class="form-group">
    <div class="col-xs-offset-2 col-xs-10">
      <button type="submit" class="btn btn-primary" name="newPass">Изменить</button>
   </div>
  </div>
</form>
  </div>
</div>
</div>

Формы мы сделали, самое время написать обработчик данных форм reminder.php

Файл: reminder.php

<?php
 /**
 * Обработчик формы восстановления пароля
 * Site: http://bezramok-tlt.ru
 * Авторизация пользователя
 */

 //Ключ защиты
 if(!defined('BEZ_KEY'))
 {
     header("HTTP/1.1 404 Not Found");
     exit(file_get_contents('./../404.html'));
 }
 
 //Выводим сообщение об отправки ссылки для восстановления пароля
 if(isset($_GET['send']) and $_GET['send'] == 'ok')
	echo '<div class="alert alert-success"><h4 class="text-center">Ваш запрос на восстановление пароля отправлен на указаный вами email!</h4></div>';

  //Выводим сообщение об успешно смене пароля
  if(isset($_GET['newpass']) and $_GET['newpass'] == 'ok')
	echo '<div class="alert alert-success"><h4 class="text-center">Ваш пароль успешно изменен, проверьте свой email!</h4></div>';

 //Если нажата кнопка восстановить пароль утюжим переменные 
 if(isset($_POST['reminder'])){
	
	//Если email существует, то проверяем есть ли он в нашей базе
	if(emailValid($_POST['email'])){
		//Запрос на выборку аккаунта для восстановления
		$sql = 'SELECT * FROM `'. BEZ_DBPREFIX .'reg`
				WHERE `status` = 1
				AND `login` = :email';

		//Подготавливаем PDO выражение для SQL запроса
		$stmt = $db->prepare($sql);
		$stmt->bindValue(':email', $_POST['email'], PDO::PARAM_STR);
		if($stmt->execute())
		{
			//Получаем ответ от MySQL
			$rows = $stmt->fetch(PDO::FETCH_ASSOC);
			
			//Проверяем что такой email есть
			if(!empty($rows))
			{
				//Шлем письмо для восстановления пароля
				$title = 'Вы запросили восстановление пароля на http://bezramok-tlt.ru';
				$message = 'Для смены пароля Вам нужно пройти по ссылке <a href="'. BEZ_HOST .'?mode=reminder&key='. $rows['active_hex'] .'">'. BEZ_HOST .'?mode=reminder&key='. $rows['active_hex'] .'</a>';
					
				sendMessageMail($_POST['email'], BEZ_MAIL_AUTOR, $title, $message);
					
				//Перенаправляем пользователя на нужную нам страницу
				header('Location:'. BEZ_HOST .'?mode=reminder&send=ok');
				exit;
			}
			else
			{
				echo showErrorMessage('Нет такого пользователя!');
			}
		}
		else
		{
				echo showErrorMessage('Чтото пошло не так :(');
		}

	}
	else
	{
		echo showErrorMessage('Не верные данные!');
	}

}

 //Если пользователь сменил пароль
if(isset($_POST['newPass']))
{
	//Утюжим переменные
	if(empty($_POST['pass']))
		$err[] = 'Поле Пароль не может быть пустым';
	
	if(empty($_POST['pass2']))
		$err[] = 'Поле Подтверждения пароля не может быть пустым';
	
	//Проверяем равенство паролей
	if($_POST['pass'] != $_POST['pass2'])
		$err[] = 'Пароли не совподают!';
	
	//Проверяем наличие ошибок и выводим пользователю
	if(count($err) > 0)
		echo showErrorMessage($err);
	else
	{
		//Получаем данные о пользователе
		$sql = 'SELECT * FROM `'. BEZ_DBPREFIX .'reg`
				WHERE `status` = 1
				AND `active_hex` = :active_hex';

		//Подготавливаем PDO выражение для SQL запроса
		$stmt = $db->prepare($sql);
		$stmt->bindValue(':active_hex', $_GET['key'], PDO::PARAM_STR);
		if($stmt->execute())
		{
			//Получаем ответ от MySQL
			$rows = $stmt->fetch(PDO::FETCH_ASSOC);
			
			//Почтовый ящик
			$email = $rows['login'];
			
			//Солим пароль
			$pass = md5(md5($_POST['pass']).$rows['salt']);
			
			//Создаем новый active_hex для защиты
			$active_hex = md5($pass);

			//Обновляем данные в таблице
			$sql = 'UPDATE `'. BEZ_DBPREFIX .'reg`
				SET 
					`pass` = :pass, 
					`active_hex` = :active_hex
				WHERE `id` = '. $rows['id'];
			
			//Подготавливаем PDO выражение для SQL запроса
			$stmt = $db->prepare($sql);
			
			//Если запрос выполнился
			if($stmt->execute(array(':pass' => $pass, ':active_hex' => $active_hex)))
			{
				//Отправляем сообщение на почту об успешной смене пароля
				$title = 'Вы успешно сменили пароль на http://bezramok-tlt.ru';
				$message = 'Вы успешно сменили пароль на '. $_POST['pass'] .'
				<p>для входа в систему перейдите по ссылке <a href="'. BEZ_HOST .'?mode=auth">'. BEZ_HOST .'?mode=auth</a></p>';
						
				sendMessageMail($email, BEZ_MAIL_AUTOR, $title, $message);
					
				//Перенаправляем пользователя на нужную нам страницу
				header('Location:'. BEZ_HOST .'?mode=reminder&newpass=ok');
				exit;
			}

		}
	}
	
}

?>

Как видите написание данного функционала совсем не сложное дело, проблема в том, что новички в программирование не могут выразить человеческим языком последовательность действий (алгоритма работы), тем самым и создаются трудности в написании того или иного скрипта. С вами был админ https://bezramok-tlt.ru до новых встреч друзья и удачного кодинга!

Архив перезалил: 02.02.2017г.
Андрей
20:44:36 12/08/2017г.
Еще такой вопрос, я добавил в сессию ID пользователя

после $_SESSION['user'] = true;

добавил

$_SESSION['user_id'] = $rows[0]['id'];

я так понимаю сессия должна работать на всех страницах

на вашей странице Index.php - user_id работает

Array(

[user] => 1

[user_id] => 4

)

я перехожу на другую страницу с кодом:

if(!$_SESSION['user'] = true)

{ header('Location:'. BEZ_HOST .'user/?mode=auth'); }

else

{

  echo "Вы авторизированы<br><br><br><pre>";

  print_r($_SESSION);

  echo "</pre>";

}

и на ней  user_id  куда-то теряется

Array(

[user] => 1

)

Что это может быть? 

Спасибо!

У Вас ошибка в условии if(!$_SESSION['user'] = true)  должен стоять оператор сравнения ==

- Администрация     - 18:10:31 29/08/2017г.

Андрей
19:39:43 12/08/2017г.
Я так понимаю при смене пароля как-то не корректно отправляется письмо.

обратный адрес BEZ_MAIL_AUTOR  в письмо не попадает, присьмо приходит без обратного адреса и попадает в спам.

Попробую найт ошибку, может вы найдете ее быстрее.

Спасибо.

Так как сервер ваш или арендованный у хостера, я не смогу ничем помочь. Т.к. в СПАМ письмо уходит благодаря фильтру почтового сервиса, например маил.ру

- Администрация     - 18:05:28 29/08/2017г.

Андрей
21:17:23 09/08/2017г.
Письмо с ссылкой для подтверждения регистрации, и после этого письмо о благополучной регистрации - приходит

А восстановление пароля - нет

Вот что удивительно.

Я на почтовом ящике гугля тестирую

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

- Администрация     - 21:21:13 09/08/2017г.

Андрей
17:18:01 08/08/2017г.
Я перешел по ссылке, открылась форма смены пароля, пароль поменял, все нормально, подтверждение измененного пароля письмо пришло, но тоже криво и тоже в спам.

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

- Администрация     - 10:45:02 09/08/2017г.

Андрей
17:14:04 08/08/2017г.
Да, в спаме, как-то криво приходит, может что-то поправить?



A message that you sent contained no recipient addresses, and therefore no

delivery could be attempted.



------ This is a copy of your message, including all the headers. ------



To:

Subject: =?utf-8?b?0JLRiyDQt9Cw0L/RgNC+0YHQuNC70Lgg0LLQvtGB0YHRgtCw0L3QvtCy0LvQtdC90LjQtS

DQv9Cw0YDQvtC70Y8g0L3QsCBodHRwOi8vYmV6cmFtb2stdGx0LnJ1?=

Content-type: text/html; charset="utf-8"

From: Регистрация на МОЙДОМЕН <no-reply@МОЙДОМЕН>

MIME-Version: 1.0

Date: Tue, 08 Aug 2017 05:08:54 +0300

Message-Id: <E1df5Bm-0001yt-1W@ef105.mirohost.net>



Для смены пароля Вам нужно пройти по ссылке <a href="МОЙДОМЕН?mode=reminder&key=115eb04179eeceeacfe30eb807f0ae7f">МОЙДОМЕН?mode

=reminder&key=115eb04179eeceeacfe30eb807f0ae7f</a>

Это вам нужно к хостеру вашему обратиться.

- Администрация     - 10:43:17 09/08/2017г.

Андрей
00:41:53 08/08/2017г.
PS

а также поля active_hex и pass видимо не меняются, так как после запроса пароля я могу зайти со старым паролем.

Поля меняются после того как смените пароль! Во время запроса пароль не меняется, мало ли вдруг вы его вспомнили )

- Администрация     - 07:18:10 08/08/2017г.

Получить уведомление на Email

Введите код с картинки