介绍 (Introduction)
Laravel form requests are special classes that extend the functionality of regular request classes, enabling advanced validation features. Form requests also help to keep your controller actions a lot cleaner, because you can move all your validation logic to the form request class. Another benefit is that it allows you to filter requests before they reach your controller actions.
Laravel 表单请求是特殊类,可扩展常规请求类的功能,从而启用高级验证功能 。 表单请求还可以使您的控制器操作更加简洁,因为您可以将所有验证逻辑移至表单请求类。 另一个好处是,它允许您在请求到达控制器操作之前对其进行过滤。
In this guide, we’ll implement a password verification step to request that a user confirms their password before accessing an admin area. This practice works as a double check and provides your application an extra layer of security.
在本指南中,我们将实施密码验证步骤,以要求用户在访问管理区域之前确认密码。 这种做法可以作为双重检查,并为您的应用程序提供额外的安全性。
先决条件 (Prerequisites)
To follow up with this guide, you’ll need a working Laravel 5.6+ application with the built-in Laravel authentication set up. Please check the official documentation for details on how to set this up.
要继续遵循本指南,您将需要一个具有内置Laravel身份验证设置的可运行Laravel 5.6+应用程序。 请检查官方文档以获取有关设置方法的详细信息。
第1步-创建视图 (Step 1 — Creating the View)
We are going to start by setting up a user’s edit profile page.
我们将从设置用户的编辑个人资料页面开始。
At the time of writing this tutorial, artisan
command utility does not generate views so we’ll need to create the view manually.
在撰写本教程时, artisan
命令实用程序不会生成视图,因此我们需要手动创建视图。
Create the file resources/views/profile/edit.blade.php
and add the following code.
创建文件resources/views/profile/edit.blade.php
并添加以下代码。
@extends('layouts.app')
@section('content')
<div class="container">
@if (session('info'))
<div class="row">
<div class="col-md-12">
<div class="alert alert-success alert-dismissible">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
{{ session('info') }}
</div>
</div>
</div>
@elseif (session('error'))
<div class="row">
<div class="col-md-12">
<div class="alert alert-danger alert-dismissible">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
{{ session('error') }}
</div>
</div>
</div>
@endif
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Update Profile</div>
<div class="panel-body">
<form class="form-horizontal" method="POST" action="{{ route('profile.update', ['user' => $user]) }}">
{{ csrf_field() }}
{{ method_field('PUT') }}
<div class="form-group{{ $errors->has('name') ? ' has-error' : '' }}">
<label for="name" class="col-md-4 control-label">Name</label>
<div class="col-md-6">
<input id="name" type="text" class="form-control" name="name" value="{{ $user->name }}">
@if ($errors->has('name'))
<span class="help-block">
<strong>{{ $errors->first('name') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group{{ $errors->has('password') ? ' has-error' : '' }}">
<label for="password" class="col-md-4 control-label">Password</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control" name="password">
@if ($errors->has('password'))
<span class="help-block">
<strong>{{ $errors->first('password') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group">
<label for="password-confirm" class="col-md-4 control-label">Confirm Password</label>
<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation">
</div>
</div>
<div class="form-group{{ $errors->has('current_password') ? ' has-error' : '' }}">
<label for="current-password" class="col-md-4 control-label">Current Password</label>
<div class="col-md-6">
<input id="current-password" type="password" class="form-control" name="current_password">
@if ($errors->has('current_password'))
<span class="help-block">
<strong>{{ $errors->first('current_password') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<button type="submit" class="btn btn-primary">
Update
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
In our “edit profile” page, we check for an info
and error
flash message and display it to the user.
在“编辑个人资料”页面中,我们检查是否有info
和error
提示信息,并将其显示给用户。
It has a name
, password
, password_confirmation
, and current_password
field.
它具有一个name
, password
, password_confirmation
和current_password
字段。
The way we want it to work is whenever a user makes a change, they have to provide the correct current_password
field to commit the update to the database.
我们希望它的工作方式是每当用户进行更改时,他们都必须提供正确的current_password
字段才能将更新提交到数据库。
The password
and password_confirmation
fields will allow the user to change their password. If they are both left empty, the user’s current password will be retained and no changes will be made to their stored password.
password
和password_confirmation
字段将允许用户更改其密码。 如果它们都保留为空,则将保留用户的当前密码,并且不会更改其存储的密码。
The major players in our view are the password
, password_confirmation
, and current_password
fields.
在我们看来,主要角色是password
, password_confirmation
和current_password
字段。
As for the name
field, it serves as an example to expand upon and add more fields for your case.
至于name
字段,它是一个示例,可以根据您的情况进行扩展并添加更多字段。
第2步-创建表单请求 (Step 2 — Creating the Form Request)
Now onto the most crucial part of this tutorial.
现在进入本教程最关键的部分。
Execute the following command to create a form request.
执行以下命令以创建表单请求。
- php artisan make:request UpdateProfile php artisan make:request UpdateProfile
The above command will create a file named app/Http/Requests/UpdateProfile.php
.
上面的命令将创建一个名为app/Http/Requests/UpdateProfile.php
。
All code changes in this section will be made to this file.
本部分中的所有代码更改都将对此文件进行。
The first thing we need to do is alias Laravel’s Hash facade before the class declaration.
我们需要做的第一件事是在类声明之前使用别名Laravel的Hash门面 。
use Illuminate\Support\Facades\Hash;
Next, we need to return true
from our authorize
method since we are not performing authorization in our form request.
接下来,由于我们不在表单请求中执行授权,因此我们需要从authorize
方法返回true
。
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
Our rules method will return the array outlining the validation rules for this request.
我们的rules方法将返回概述此请求验证规则的数组。
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => 'required|string|max:255',
'password' => 'nullable|required_with:password_confirmation|string|confirmed',
'current_password' => 'required',
];
}
The name
and current_password
rules are self-explanatory.
name
和current_password
规则不言自明。
The password
rules states that the password will be confirmed using the confirmed
declaration.
password
规则指出,将使用已confirmed
声明来确认密码。
It also declares required_with:password_confirmation
which means if the user provides a password confirmation, they should also provide a password.
它还声明了required_with:password_confirmation
,这意味着如果用户提供密码确认,则他们还应该提供密码。
These validation rules will be checked automatically upon every request once we type-hint it in our controller action(which we’ll do later).
一旦在控制器操作中键入提示,这些验证规则将在每次请求时自动检查(我们将在以后做)。
The last thing we need to do is declare a withValidator
method on our request which is passed the fully constructed validator instance before any of the validation rules fire.
我们需要做的最后一件事是在我们的请求上声明一个withValidator
方法,该方法将在触发任何验证规则之前通过完全构造的验证器实例。
/**
* Configure the validator instance.
*
* @param \Illuminate\Validation\Validator $validator
* @return void
*/
public function withValidator($validator)
{
// checks user current password
// before making changes
$validator->after(function ($validator) {
if ( !Hash::check($this->current_password, $this->user()->password) ) {
$validator->errors()->add('current_password', 'Your current password is incorrect.');
}
});
return;
}
Inside our withValdator
method, we have added an after
hook, a function which will be executed after all the validation checks have been made.
在withValdator
方法内部,我们添加了一个after
钩子,该函数将在完成所有验证检查之后执行。
In our after
hook, we have compared the user’s provided password with their password set in the database.
在after
挂钩中,我们将用户提供的密码与他们在数据库中设置的密码进行了比较。
The $this->current_password
gives us the current_password
form field value whereas Laravel allows us to access the currently authenticated user using $this->user()
so $this->user()->password
gives us the user’s hashed password saved in the database.
$this->current_password
为我们提供了current_password
表单字段值,而Laravel允许我们使用$this->user()
访问当前经过身份验证的用户,因此$this->user()->password
为我们提供了用户的哈希密码保存在数据库。
The two passwords are compared using the Hash
facade’s check
method.
使用Hash
外观的check
方法比较这两个密码。
If the hash check fails, an error is added to the validator with the key current_password
using $validator->errors()->add('current_password', 'Your current password is incorrect.')
.
如果哈希检查失败,则使用$validator->errors()->add('current_password', 'Your current password is incorrect.')
使用密钥current_password
将错误添加到验证器。
Here is our complete UpdateProfile
form request.
这是我们完整的UpdateProfile
表单请求。
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Hash;
class UpdateProfile extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => 'required|string|max:255',
'password' => 'nullable|required_with:password_confirmation|string|confirmed',
'current_password' => 'required',
];
}
/**
* Configure the validator instance.
*
* @param \Illuminate\Validation\Validator $validator
* @return void
*/
public function withValidator($validator)
{
// checks user current password
// before making changes
$validator->after(function ($validator) {
if ( !Hash::check($this->current_password, $this->user()->password) ) {
$validator->errors()->add('current_password', 'Your current password is incorrect.');
}
});
return;
}
}
步骤3 —设置控制器 (Step 3 — Setting Up the Controller)
To use our form request, we’ll need to type-hint it in our controller action.
要使用表单请求,我们需要在控制器操作中键入提示。
Execute the following command to generate the profile controller.
执行以下命令以生成概要文件控制器。
- php artisan make:controller ProfileController php artisan make:controller ProfileController
Open the file app/Http/Controllers/ProfileController.php
and add the following controller actions.
打开文件app/Http/Controllers/ProfileController.php
并添加以下控制器操作。
public function __construct()
{
$this->middleware('auth');
}
/**
* Show the form for editing the specified resource.
*
* @param \App\User $user
* @return \Illuminate\Http\Response
*/
public function edit(Request $request, User $user)
{
// user
$viewData = [
'user' => $user,
];
// render view with data
return view('profile.edit', $viewData);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param \App\User $user
* @return \Illuminate\Http\Response
*/
public function update(UpdateProfile $request, User $user)
{
// form data
$data = $request->all();
$user->update($data);
return redirect(route('profile.edit', ['user' => $user]))
->with('info', 'Your profile has been updated successfully.');
}
The profile controller’s constructor sets the auth
middleware to make sure a user is logged in before editing his/her profile.
概要文件控制器的构造函数设置auth
中间件,以确保用户在编辑其概要文件之前已登录。
The edit
action serves the view with the view data whereas the update
action updates the user profile and redirects back to the edit profile page with the corresponding flash message.
edit
操作将视图数据提供给视图,而update
操作将更新用户配置文件,并使用相应的刷新消息重定向回编辑配置文件页面。
Notice the signature of the edit
action where we have type-hinted the UpdateProfile
request which is all we need to do to fire the validations inside our UpdateProfile
form request.
请注意,在我们键入提示UpdateProfile
请求的位置上执行edit
动作的签名,这是我们在UpdateProfile
表单请求内触发验证所需要做的全部工作。
You will also need to alias the form request and the user model before the controller class declaration.
您还需要在控制器类声明之前为表单请求和用户模型添加别名。
use App\Http\Requests\UpdateProfile;
use App\User;
步骤4 —设置受保护的路由和数据变量 (Step 4 — Setting up Protected Routes and Data Mutators)
Open the file app/routes/web.php
and add the following code to tie in the controller actions.
打开文件app/routes/web.php
并添加以下代码以绑定控制器操作。
Route::get('/profile/{user}/edit', 'ProfileController@edit')->name('profile.edit');
Route::put('/profile/{user}', 'ProfileController@update')->name('profile.update');
Based on the validation rule we added earlier to our form request, it is possible for a null
password to pass through.
根据我们先前在表单请求中添加的验证规则,可能会传递null
密码。
Under no circumstances would a user(or an application developer) want their password to be set to null
or an empty string.
用户(或应用程序开发人员)在任何情况下都不会希望将其密码设置为null
或空字符串。
To make sure a user password is set only when they provide one, we are going to use Eloquent ORM’s mutators.
为了确保仅在提供用户密码时设置用户密码,我们将使用Eloquent ORM的mutators 。
Open the file app/User.php
and add the following code.
打开文件app/User.php
并添加以下代码。
// Only accept a valid password and
// hash a password before saving
public function setPasswordAttribute($password)
{
if ( $password !== null & $password !== "" )
{
$this->attributes['password'] = bcrypt($password);
}
}
Eloquent mutators have to follow the naming scheme set<camel-cased-attribute-name>Attribute
.
雄辩的更改者必须遵循命名方案set<camel-cased-attribute-name>Attribute
。
Since we are declaring a mutator for the password
attribute hence we have named the mutator setPasswordAttribute
.
由于我们为password
属性声明了一个变量,因此我们将变量命名为setPasswordAttribute
。
The mutator function is passed the value being set which in our mutator is the $password
variable.
传递给mutator函数的是所设置的值,该值在我们的mutator中是$password
变量。
In our mutator, we check if the $password
variable is not null or an empty string and set it in our model using $this->attributes['password']
.
在增变器中,我们检查$password
变量是否为null或为空字符串,并在模型中使用$this->attributes['password']
。
Also note the password is hashed before saving so we do not have to do it elsewhere in our application.
还要注意,密码在保存之前已被哈希处理,因此我们不必在应用程序的其他位置进行密码处理。
The default Laravel Auth/RegisterController
and Auth/ResetPasswordController
also hash the password before persisting it to the database so we need to update the create
and resetPassword
method in the respective controllers after declaring the above mutator.
默认的Laravel Auth/RegisterController
和Auth/ResetPasswordController
在将密码持久保存到数据库之前,还会对密码进行哈希处理,因此,我们需要在声明上述变体之后在相应的控制器中更新create
和resetPassword
方法。
Open the file app/Http/Controllers/Auth/RegisterController.php
and add the following code.
打开文件app/Http/Controllers/Auth/RegisterController.php
并添加以下代码。
/**
* Create a new user instance after a valid registration.
*
* @param array $data
* @return \App\User
*/
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => $data['password'],
]);
}
Open the file app/Http/Controllers/Auth/ResetPasswordController.php
and add the following code.
打开文件app/Http/Controllers/Auth/ResetPasswordController.php
并添加以下代码。
/**
* Reset the given user's password.
*
* @param \Illuminate\Contracts\Auth\CanResetPassword $user
* @param string $password
* @return void
*/
protected function resetPassword($user, $password)
{
$user->password = $password;
$user->setRememberToken(Str::random(60));
$user->save();
event(new PasswordReset($user));
$this->guard()->login($user);
}
For the ResetPasswordController
, you will also need to alias the respective classes used before the class declaration.
对于ResetPasswordController
,您还需要为类声明之前使用的相应类添加别名。
use Illuminate\Support\Str;
use Illuminate\Auth\Events\PasswordReset;
We are all done and our password verification works as expected.
我们已经完成了所有工作,并且密码验证按预期进行了。
A test run of the application when providing an incorrect password or none yields the following behaviour demonstrated by the screenshots.
当提供错误密码或不提供密码时,对应用程序进行测试运行会产生以下行为,如屏幕截图所示。
结论 (Conclusion)
In this guide, we saw how to implement an additional password verification step to assert that a user is authorized to access an admin area. We’ve seen how to create and set up form requests to implement form validation within a Laravel application.
在本指南中,我们看到了如何执行额外的密码验证步骤来断言用户已被授权访问管理区域。 我们已经看到了如何创建和设置表单请求以在Laravel应用程序中实现表单验证。
For more information about validation and form requests, you can check the official Laravel documentation.
有关验证和表单请求的更多信息,您可以查看Laravel官方文档 。
翻译自: https://www.digitalocean.com/community/tutorials/how-to-implement-password-verification-using-laravel-form-request