Переработка регистрации и аутентификации в единую модель

This commit is contained in:
RedHood 2020-12-25 11:14:41 +10:00
parent e0a6ae88e4
commit 7d27736a02
12 changed files with 409 additions and 294 deletions

View File

@ -5,7 +5,7 @@ $config = [
'basePath' => dirname(__DIR__),
'bootstrap' => ['log'],
'aliases' => [
'@vendor' => dirname(__DIR__) . '../../../../vendor',
'@vendor' => dirname(__DIR__) . '/../../../vendor',
'@bower' => '@vendor/bower-asset',
'@npm' => '@vendor/npm-asset',
'@explosivebit' => '@vendor/explosivebit',

View File

@ -7,10 +7,8 @@ use yii\filters\AccessControl;
use yii\web\Controller;
use yii\web\Response;
use yii\filters\VerbFilter;
use app\models\AuthenticationForm;
use app\models\RegistrationForm;
use app\models\AccountForm;
use app\models\ContactForm;
use app\models\Account;
class SiteController extends Controller
{
@ -106,13 +104,15 @@ class SiteController extends Controller
*/
public function actionAuthentication()
{
$model = new AuthenticationForm(Yii::$app->request->post()['AuthenticationForm'] ?? Yii::$app->request->get()['AuthenticationForm']);
$model = new AccountForm(Yii::$app->request->post()['AccountForm'] ?? Yii::$app->request->get()['AccountForm'] ?? null);
if (Yii::$app->request->isAjax) {
// AJAX-POST-запрос
Yii::$app->response->format = Response::FORMAT_JSON;
if (!Yii::$app->user->isGuest) {
// Аккаунт уже авторизован
// Аккаунт уже аутентифицирован
Yii::$app->response->statusCode = 403;
return [
'form' => $this->renderPartial('index'),
@ -120,10 +120,9 @@ class SiteController extends Controller
];
}
Yii::$app->response->format = Response::FORMAT_JSON;
if ($model->authentication()) {
// Данные прошли проверку
if (isset($model->mail, $model->pswd)) {
if ($model->load(Yii::$app->request->post()) && $model->authentication()) {
return [
'menu' => <<<HTML
<p class="m-0">
@ -135,11 +134,13 @@ class SiteController extends Controller
'form' => $this->renderPartial('index'),
'_csrf' => Yii::$app->request->getCsrfToken()
];
}
} else {
// Данные не прошли проверку
Yii::$app->response->statusCode = 400;
return [
'menu' => '<a onclick="authentication()">Вход</a>',
'form' => $this->renderPartial('authentication', compact('model')),
'form' => $this->renderPartial('account', compact('model')),
'_csrf' => Yii::$app->request->getCsrfToken()
];
}
@ -149,7 +150,7 @@ class SiteController extends Controller
// GET-запрос и прочие
if (!Yii::$app->user->isGuest) {
// Аккаунт уже авторизован
// Аккаунт уже аутентифицирован
Yii::$app->response->redirect('/');
}
}
@ -172,7 +173,6 @@ class SiteController extends Controller
return [
'menu' => '<a onclick="authentication()">Вход</a>',
'form' => $this->renderPartial('index'),
'_csrf' => Yii::$app->request->getCsrfToken()
];
}
@ -187,13 +187,16 @@ class SiteController extends Controller
*/
public function actionRegistration()
{
$model = new RegistrationForm(Yii::$app->request->post()['RegistrationForm'] ?? Yii::$app->request->get()['RegistrationForm']);
$model = new AccountForm(Yii::$app->request->post()['AccountForm'] ?? Yii::$app->request->get()['AccountForm'] ?? null);
$model->type = 0;
if (Yii::$app->request->isAjax) {
// AJAX-POST-запрос
Yii::$app->response->format = Response::FORMAT_JSON;
if (!Yii::$app->user->isGuest) {
// Аккаунт уже авторизован
// Аккаунт уже аутентифицирован
Yii::$app->response->statusCode = 302;
return [
'form' => $this->renderPartial('index'),
@ -201,17 +204,7 @@ class SiteController extends Controller
];
}
Yii::$app->response->format = Response::FORMAT_JSON;
if (isset($model->mail, $model->pswd)) {
// Аккаунт передал необходимые параметры
// Инициализация нового аккаунта
$account = new Account();
$account->mail = $model->mail;
$account->pswd = Yii::$app->security->generatePasswordHash($model->pswd);
if ($model->load(Yii::$app->request->post()) && $model->validate() && $account->save()) {
if ($model->registration()) {
// Данные прошли проверку и аккаунт был создан
return [
@ -219,7 +212,7 @@ class SiteController extends Controller
<p class="m-0">
<a class="text-dark" href="/cart"><i class="fas fa-shopping-cart mr-4"></i></a>
<a class="text-dark" href="/orders"><i class="fas fa-list mr-4"></i></a>
<a class="text-dark" onclick="deauthentication()">Выход ($account->mail)</a>
<a class="text-dark" onclick="deauthentication()">Выход ($model->mail)</a>
</p>
HTML,
'form' => $this->renderPartial('index'),
@ -231,16 +224,7 @@ class SiteController extends Controller
Yii::$app->response->statusCode = 400;
return [
'form' => $this->renderPartial('registration', compact('model')),
'_csrf' => Yii::$app->request->getCsrfToken()
];
}
} else {
// Аккаунт не передал необходимые параметры
return [
'menu' => '<a onclick="authentication()">Вход</a>',
'form' => $this->renderPartial('registration', compact('model')),
'form' => $this->renderPartial('account', compact('model')),
'_csrf' => Yii::$app->request->getCsrfToken()
];
}
@ -251,7 +235,7 @@ class SiteController extends Controller
// GET-запрос и прочие
if (!Yii::$app->user->isGuest) {
// Аккаунт уже авторизован
// Аккаунт уже аутентифицирован
Yii::$app->response->redirect('/');
}
}
@ -259,6 +243,18 @@ class SiteController extends Controller
return $this->render('registration', compact('model'));
}
/**
* Displays профиль
*
* @return Response|string
*/
public function actionProfile()
{
$model = new Account();
return $this->render('profile', compact('model'));
}
/**
* Displays contact page.
*

View File

@ -57,7 +57,7 @@ class Account extends ActiveRecord implements IdentityInterface
*/
public static function findIdentity($mail)
{
return static::findOne(['mail' => $mail]);
return static::findByMail($mail);
}
/**
@ -82,6 +82,23 @@ class Account extends ActiveRecord implements IdentityInterface
return static::findOne(['mail' => $mail]);
}
/**
* Validates mail
*
* @param string $pswd password to validate
* @return bool if password provided is valid for current user
*/
public function validateMail($mail)
{
if (static::findByMail($mail)) {
// Почта найдена в базе данных
return false;
}
return true;
}
/**
* Validates password
*

View File

@ -0,0 +1,151 @@
<?php
namespace app\models;
use Yii;
use yii\base\Model;
use app\models\Account;
/**
* AccountForm is the model behind the login form.
*
* @property-read Account|null $account This property is read-only.
*
*/
class AccountForm extends Model
{
public $mail;
public $pswd;
public $auto = true;
/**
* Тип обработки
*
* Регистрация: 0
* Аутентификация: 1
*
* @var integer
*/
public $type = 1;
private $account = false;
/**
* @return array the validation rules.
*/
public function rules()
{
return [
// Обязательные поля
[['mail', 'pswd'], 'required', 'message' => 'Заполните поле'],
// Функция "Запомнить меня"
['auto', 'boolean'],
// Проверка почты
['mail', 'validateMail', 'message'=>'Неправильная почта'],
// Проверка пароля
['pswd', 'validatePassword', 'message'=>'Неправильный пароль'],
];
}
public function attributeLabels()
{
return [
'mail' => 'Почта',
'pswd' => 'Пароль',
'auto' => 'Запомнить'
];
}
/**
* @param string $attribute the attribute currently being validated
* @param array $params the additional name-value pairs given in the rule
*/
public function validateMail($attribute, $params)
{
if (!$this->hasErrors() && $this->type === 0) {
// Проблем нет, обрабатывается событие регистрации
$account = $this->getAccount();
if (!$account || !$account->validateMail($this->mail)) {
// Проверка не пройдена
$this->addError($attribute, 'Почта уже привязана к другому аккаунту');
}
}
}
/**
* Validates the password.
* This method serves as the inline validation for password.
*
* @param string $attribute the attribute currently being validated
* @param array $params the additional name-value pairs given in the rule
*/
public function validatePassword($attribute, $params)
{
if (!$this->hasErrors() && $this->type === 1) {
// Проблем нет, обрабатывается событие аутентификации
$account = $this->getAccount();
if (!$account || !$account->validatePassword($this->pswd)) {
// Проверка не пройдена
$this->addError($attribute, 'Проверьте входные данные');
}
}
}
/**
* Logs in a account using the provided accountname and password.
* @return bool whether the account is logged in successfully
*/
public function authentication()
{
if (isset($this->mail, $this->pswd) && $this->validate()) {
// Проверка пройдена
// Аутентификация
return Yii::$app->user->login($this->getAccount(), $this->auto ? 3600*24*30 : 0);
}
return false;
}
/**
* @return bool
*/
public function registration()
{
// Инициализация нового аккаунта
$this->account = new Account();
if (isset($this->mail, $this->pswd) && $this->validate()) {
// Проверка пройдена
// Запись параметров
$this->account->mail = $this->mail;
$this->account->pswd = Yii::$app->security->generatePasswordHash($this->pswd);
// Регистрация
return $this->account->save();
}
return false;
}
/**
* Finds account by [[accountname]]
*
* @return Account|null
*/
public function getAccount()
{
if ($this->account === false) {
$this->account = Account::findByMail($this->mail);
}
return $this->account;
}
}

View File

@ -1,91 +0,0 @@
<?php
namespace app\models;
use Yii;
use yii\base\Model;
use app\models\Account;
/**
* LoginForm is the model behind the login form.
*
* @property-read Account|null $account This property is read-only.
*
*/
class AuthenticationForm extends Model
{
public $mail;
public $pswd;
public $auto = true;
private $account = false;
/**
* @return array the validation rules.
*/
public function rules()
{
return [
// accountname and password are both required
[['mail', 'pswd'], 'required'],
// rememberMe must be a boolean value
['auto', 'boolean'],
// pswd is validated by validatePassword()
['pswd', 'validatePassword'],
];
}
public function attributeLabels()
{
return [
'mail' => 'Почта',
'pswd' => 'Пароль',
'auto' => 'Запомнить'
];
}
/**
* Validates the password.
* This method serves as the inline validation for password.
*
* @param string $attribute the attribute currently being validated
* @param array $params the additional name-value pairs given in the rule
*/
public function validatePassword($attribute, $params)
{
if (!$this->hasErrors()) {
$account = $this->getAccount();
if (!$account || !$account->validatePassword($this->pswd)) {
$this->addError($attribute, 'Не удалось идентифицировать');
}
}
}
/**
* Logs in a account using the provided accountname and password.
* @return bool whether the account is logged in successfully
*/
public function authentication()
{
if ($this->validate()) {
return Yii::$app->user->login($this->getAccount(), $this->auto ? 3600*24*30 : 0);
}
return false;
}
/**
* Finds account by [[accountname]]
*
* @return Account|null
*/
public function getAccount()
{
if ($this->account === false) {
$this->account = Account::findByMail($this->mail);
}
return $this->account;
}
}

View File

@ -1,28 +0,0 @@
<?php
namespace app\models;
use yii\base\Model;
use app\models\Account;
class RegistrationForm extends Model
{
public $mail;
public $pswd;
public function rules()
{
return [
[['mail', 'pswd'], 'required', 'message' => 'Заполните поле'],
['mail', 'unique', 'targetClass' => Account::class, 'message' => 'Почта уже привязана к другому аккаунту'],
];
}
public function attributeLabels()
{
return [
'mail' => 'Почта',
'pswd' => 'Пароль',
];
}
}

View File

@ -0,0 +1,34 @@
<?php
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
use app\models\RegistrationForm;
?>
<div class="container d-flex flex-column">
<div class="row my-auto">
<div class="col-lg-4 mx-auto">
<?php $form = ActiveForm::begin([
'id' => 'form_account',
'action' => false,
'fieldConfig' => [
'template' => "{label}\n<div>{input}</div>\n<div>{error}</div>"
],
'options' => [
'onsubmit' => 'return false;'
]
]); ?>
<?= $form->field($model, 'mail')->textInput(['autofocus' => true]) ?>
<?= $form->field($model, 'pswd')->passwordInput() ?>
<?= $form->field($model, 'auto')->checkbox() ?>
<div class="d-flex">
<?= Html::submitButton('Войти', ['name' => 'submit', 'onclick' => 'authentication(this.parentElement.parentElement);', 'class' => 'btn btn-primary col-4']) ?>
<?= Html::submitButton('Регистрация', ['name' => 'submit', 'onclick' => 'registration(this.parentElement.parentElement);', 'class' => 'btn btn-success col-7 ml-auto']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
</div>
</div>

View File

@ -1,51 +0,0 @@
<?php
/* @var $this yii\web\View */
/* @var $form yii\bootstrap\ActiveForm */
/* @var $model app\models\LoginForm */
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
$this->title = 'Login';
?>
<div class="site-login">
<h1><?= Html::encode($this->title) ?></h1>
<p>Please fill out the following fields to login:</p>
<?php $form = ActiveForm::begin([
'id' => 'form_authentication',
'layout' => 'horizontal',
'fieldConfig' => [
'template' => "{label}\n<div class=\"col-lg-3\">{input}</div>\n<div class=\"col-lg-8\">{error}</div>",
'labelOptions' => ['class' => 'col-lg-1 control-label'],
],
'action' => false,
'options' => [
'onsubmit' => 'authentication(this); return false;'
]
]); ?>
<?= $form->field($model, 'mail')->textInput(['autofocus' => true]) ?>
<?= $form->field($model, 'pswd')->passwordInput() ?>
<?= $form->field($model, 'auto')->checkbox([
'template' => "<div class=\"col-lg-offset-1 col-lg-3\">{input} {label}</div>\n<div class=\"col-lg-8\">{error}</div>",
]) ?>
<div class="form-group">
<div class="col-lg-offset-1 col-lg-11">
<?= Html::submitButton('Login', ['class' => 'btn btn-primary', 'name' => 'login-button']) ?>
</div>
</div>
<?php ActiveForm::end(); ?>
<div class="col-lg-offset-1" style="color:#999;">
You may login with <strong>admin/admin</strong> or <strong>demo/demo</strong>.<br>
To modify the username/password, please check out the code <code>app\models\User::$users</code>.
</div>
</div>

View File

@ -0,0 +1,63 @@
<?php
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
use yii\captcha\Captcha;
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="site-contact">
<h1><?= Html::encode($this->title) ?></h1>
<?php if (Yii::$app->session->hasFlash('contactFormSubmitted')): ?>
<div class="alert alert-success">
Thank you for contacting us. We will respond to you as soon as possible.
</div>
<p>
Note that if you turn on the Yii debugger, you should be able
to view the mail message on the mail panel of the debugger.
<?php if (Yii::$app->mailer->useFileTransport): ?>
Because the application is in development mode, the email is not sent but saved as
a file under <code><?= Yii::getAlias(Yii::$app->mailer->fileTransportPath) ?></code>.
Please configure the <code>useFileTransport</code> property of the <code>mail</code>
application component to be false to enable email sending.
<?php endif; ?>
</p>
<?php else: ?>
<p>
If you have business inquiries or other questions, please fill out the following form to contact us.
Thank you.
</p>
<div class="row">
<div class="col-lg-5">
<?php $form = ActiveForm::begin(['id' => 'contact-form']); ?>
<?= $form->field($model, 'name')->textInput(['autofocus' => true]) ?>
<?= $form->field($model, 'email') ?>
<?= $form->field($model, 'subject') ?>
<?= $form->field($model, 'body')->textarea(['rows' => 6]) ?>
<?= $form->field($model, 'verifyCode')->widget(Captcha::className(), [
'template' => '<div class="row"><div class="col-lg-3">{image}</div><div class="col-lg-6">{input}</div></div>',
]) ?>
<div class="form-group">
<?= Html::submitButton('Submit', ['class' => 'btn btn-primary', 'name' => 'contact-button']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
</div>
<?php endif; ?>
</div>

View File

@ -1,21 +0,0 @@
<?php
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
?>
<?php $form = ActiveForm::begin([
'id' => 'form_registration',
'action' => false,
'options' => [
'onsubmit' => 'registration(this); return false;'
]
]) ?>
<?= $form->field($model, 'mail') ?>
<?= $form->field($model, 'pswd')->passwordInput() ?>
<div class="form-group">
<div>
<?= Html::submitButton('Регистрация', ['class' => 'btn btn-success']) ?>
</div>
</div>
<?php ActiveForm::end() ?>

View File

@ -25,6 +25,7 @@ body {
main {
flex-grow: 1;
display: grid;
background-color: #f0eefb;
}

View File

@ -3,21 +3,26 @@ function identification() {
url: '/identification',
type: 'post',
dataType: 'json',
data: { '_csrf': yii.getCsrfToken() },
data: {
'_csrf': yii.getCsrfToken()
},
success: function (data) {
// Обновление документа
if (data.menu != undefined) {
document.getElementById('nav').innerHTML = data.menu;
}
if (data._csrf != undefined) {
$('meta[name=csrf-token]').prop("content", data._csrf);
// Реинициализация
reinitialization();
}
}
});
};
function authentication(form) {
if (form == undefined) {
form = { '_csrf': yii.getCsrfToken() };
form = {
'_csrf': yii.getCsrfToken()
};
} else {
form = $(form).serialize();
}
@ -29,9 +34,15 @@ function authentication(form) {
data: form,
success: function (data, status) {
// Обновление документа
if (data.menu != undefined) {
document.getElementById('nav').innerHTML = data.menu;
}
if (data.form != undefined) {
document.getElementsByTagName('main')[0].innerHTML = data.form;
}
if (data._csrf != undefined) {
$('meta[name=csrf-token]').prop("content", data._csrf);
}
// Перенаправление
history.pushState({}, document.title, '/');
@ -40,11 +51,18 @@ function authentication(form) {
reinitialization();
},
error: function (data, status) {
if (data.responseJSON.form != undefined) {
// Обновление документа
document.getElementsByTagName('main')[0].innerHTML = data.responseJSON.form;
// Реинициализация
reinitialization();
}
if (data.responseJSON._csrf != undefined) {
// Обновление документа
$('meta[name=csrf-token]').prop("content", data.responseJSON._csrf);
}
if (statis === 403) {
if (status === 403) {
// Перенаправление
history.pushState({}, document.title, '/');
}
@ -62,50 +80,76 @@ function deauthentication() {
dataType: 'json',
data: { '_csrf': yii.getCsrfToken() },
success: function (data) {
if (data.menu != undefined) {
// Обновление документа
document.getElementById('nav').innerHTML = data.menu;
}
if (data.form != undefined) {
// Обновление документа
document.getElementsByTagName('main')[0].innerHTML = data.form;
// Реинициализация
reinitialization();
}
if (data._csrf != undefined) {
// Обновление документа
$('meta[name=csrf-token]').prop("content", data._csrf);
}
// Перенаправление
history.pushState({}, document.title, '/');
// Реинициализация
reinitialization();
}
});
};
function registration(form) {
if (form == undefined) {
form = {
'_csrf': yii.getCsrfToken()
};
} else {
form = $(form).serialize();
}
$.ajax({
url: '/registration',
type: 'post',
dataType: 'json',
data: $(form).serialize(),
data: form,
success: function (data) {
if (data.menu != undefined) {
// Обновление документа
document.getElementById('nav').innerHTML = data.menu;
}
if (data.form != undefined) {
// Обновление документа
document.getElementsByTagName('main')[0].innerHTML = data.form;
// Реинициализация
reinitialization();
}
if (data._csrf != undefined) {
// Обновление документа
$('meta[name=csrf-token]').prop("content", data._csrf);
}
// Перенаправление
history.pushState({}, document.title, '/');
// Реинициализация
reinitialization();
},
error: function (data) {
if (data.responseJSON.form != undefined) {
// Обновление документа
document.getElementsByTagName('main')[0].innerHTML = data.responseJSON.form;
// Реинициализация
reinitialization();
}
if (data.responseJSON._csrf != undefined) {
// Обновление документа
$('meta[name=csrf-token]').prop("content", data.responseJSON._csrf);
}
if (statis === 403) {
// Перенаправление
history.pushState({}, document.title, '/');
}
// Реинициализация
reinitialization();
}
});
};