User authentication
brief introduction
Note: Want to get started quickly? It needs to be installed through Composer under the newly installed Larravel application laravel/ui
Expansion pack, then run php artisan ui vue --auth
Initialize the front-end scaffold code, and then run it npm install && npm run dev
Install the front-end dependency and compile the front-end resources, and finally run php artisan migrate
Execute database migration to generate the user table. At this time, the fully functional user registration and login function has been completed, which can be accessed in the browser http://your-app.test/register
You can see the user registration interface.
It is very simple to implement login authentication in Laravel. In fact, Larave has already configured almost everything for you. The configuration file is located in config/auth.php
, which contains document friendly option configurations for adjusting the authentication service behavior.
In the underlying code, the authentication component of Laravel consists of "guards" and "providers". Guard defines how the user implements authentication in each request. For example, Laravel session
Guard to maintain the status of session storage and cookies.
The provider defines how to obtain user information from the persistent storage. The bottom layer of Laravel supports obtaining users through Eloquent and database query builder. If necessary, you can also define additional providers.
If you see these terms, you don't have to worry too much, because for most applications, you only need to use the default authentication configuration and do not need to make any changes.
College note: Generally speaking, when performing login authentication, two things should be done: one is to access user data from the database, the other is to save the user login status. In the underlying implementation of Laravel, data is accessed through the provider, and user authentication information is stored through the Guard. The former mainly deals with the database, The latter mainly deals with session (API exception).
Database considerations
By default, Larave app
The directory contains a Eloquent model App\User
This model can be used with the default Eloquent authentication driver. If your app doesn't use Eloquent, you can use database
Authentication driver, which uses Larravel query builder to interact with database.
by App\User
When building the database table structure, ensure that password
The field length must be at least 60 bits. Keeping the default string length (255) is a good choice.
Also, you need to verify users
Table contains remember_token
, this field is a string type that can be empty. The field length is 100. It is used to store the "Remember Me" session token maintained by the application during login.
quick get start
Laravel provides several preset authentication controllers located in App\Http\Controllers\Auth
Namespace, RegisterController
Used to process new user registration, LoginController
It is used to process user login authentication, ForgotPasswordController
Used to process the reset password email link, ResetPasswordController
It contains password reset logic. Each controller uses traits to introduce the methods they need. For many applications, you don't need to modify these controllers at all:
route
Based on the laravel/ui
For the expansion package, we can quickly generate all the routes and views required for user authentication by running the following command (ignore those that have already been run):
composer require laravel/ui php artisan ui vue --auth
This command needs to be run in the newly installed application. After running, the layout, registration and login views, as well as all authentication routes, will be generated HomeController
It is used to process the login request of the application.
open routes/web.php
Two new lines are found in the routing file:
Auth::routes(); Route::get('/home', ' HomeController@index ')->name('home');
Routes related to login and registration are defined above Auth::routes()
Method.
Note: If your application does not need to be registered, you can remove the newly created RegisterController
Controller and edit route definition to prohibit registration: Auth::routes(['register' => false]);
。
Create an app that contains an authentication code
If you want to include all the certification scaffolding codes when initializing a new application, you can use them when creating a new application --auth
Instruction. In this way, we do not need to do the above authentication initialization:
laravel new blog --auth
view
As mentioned above, laravel/ui
Package provided php artisan ui vue --auth
The command will be resources/views/auth
Create all the views required for authentication under the directory.
ui
The command also creates resources/views/layouts
Directory, which contains the applied basic layout files. All these views use the Bootstrap CSS framework, which you can customize as needed.
authentication
Now that you have set the route and view for your own authentication controller, let's implement new user registration and login authentication. You can access the defined route in the browser. By default, the authentication controller already contains registration and login logic (through trait).
Let's first register a new user and access it in the browser http://blog.test/register
, you can enter the registration page:
Fill in the form and click the "Register" button to complete the registration. After successful registration, the page will jump to the page after authentication http://blog.test/home
:
To test the login function, you can exit the current user first and then visit the login page http://blog.test/login
:
After successfully logging in with our previously registered information, we will also jump to http://blog.test/home
。
Custom Path
We already know that when a user successfully performs login authentication, the default will be to /home
, you can use the RouteServiceProvider
Used in HOME
Constants customize the redirected path after authentication:
public const HOME = '/home';
If you need to completely customize the response returned after user authentication, you can implement the null method provided by Laravel authenticated(Request $request, $user)
To complete:
/** * The user has been authenticated. * * @param \Illuminate\Http\Request $request * @param mixed $user * @return mixed */ protected function authenticated(Request $request, $user) { return response([ // ]); }
Custom User Name
By default, Larravel uses email
Field for authentication. If you want to customize the authentication field, you can LoginController
Defined in username
method:
public function username() { return 'username'; }
Custom Guard
You can also customize the "guard" for user registration and login. To achieve this function, you need to LoginController
、 RegisterController
and ResetPasswordController
Defined in guard
Method, which will return a guard instance:
use Illuminate\Support\Facades\Auth; protected function guard() { return Auth::guard('guard-name'); }
It should be noted that the "guard" name should be in the configuration file config/auth.php
Configured in:
'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'token', 'provider' => 'users', 'hash' => false, ], ],
Custom Authentication/Storage
To modify the form fields necessary for new user registration, or customize how new user fields are stored in the database, you can modify RegisterController
Class. This class is responsible for validating input parameters and creating new users for applications.
RegisterController
Of validator
Method contains the validation rules for new user registration. You can customize this method as needed.
RegisterController
Of create
Method responsible for use Eloquent ORM Create a new App\User
record. Of course, you can also customize this method based on your own needs.
Get login user
You can Auth
Facade access authentication user:
use Illuminate\Support\Facades\Auth; //Get current authenticated user $user = Auth::user(); //Get the ID of the current authenticated user $id = Auth::id();
In addition, after the user passes the authentication, you can also pass the Illuminate\Http\Request
Instance access authentication user (the type prompt class will be automatically injected into the controller method through dependency injection):
<? php namespace App\Http\Controllers; use Illuminate\Http\Request; class ProfileController extends Controller{ /** *Update user attributes * * @param Request $request * @return Response */ public function update(Request $request) { //$request ->user() returns the authenticated user instance } }
The results returned by the above two methods are identical:
Judge whether the current user has passed the authentication
To determine whether a user logs in to the application, you can use the Auth
Facade check
Method. If the user passes authentication, return true
:
use Illuminate\Support\Facades\Auth; if (Auth::check()) { // The user is logged in... }
Note: Although we can use check
The method is to determine whether the user passes the authentication, but we usually use middleware to verify whether the user passes the authentication before accessing a specific route/controller. For more information, see the following Route protection 。
Route protection
Routing middleware Can be used to allow only authenticated users to access a given route. Laravel defines Illuminate\Auth\Middleware\Authenticate
In auth
Middleware to realize this function. Since the middleware has been registered in the HTTP kernel, all you need to do is add the middleware to the corresponding route definition:
Route::get('profile', function() { //Only authenticated users can enter })->middleware('auth');
Of course, if you can also controller Called in the constructor of middleware
Instead of defining and implementing the same function directly in the router:
public function __construct(){ $this->middleware('auth'); }
For example, our HomeController
That's what we did:
class HomeController extends Controller { /** * Create a new controller instance. * * @return void */ public function __construct() { $this->middleware('auth'); } ...
If we visit without logging in http://blog.test/home
The page will redirect to the login page.
Redirect unauthenticated users
When auth
If the middleware judges that a user is not authenticated, it will return a JSON four hundred and one
Response, or if it is not an Ajax request, redirect the user to login
Named Routes (that is, the login page).
You can update app/Http/Middleware/Authenticate.php
In file redirectTo
Function to change this behavior:
/** * Get the path the user should be redirected to. * * @param \Illuminate\Http\Request $request * @return string */ protected function redirectTo($request) { return route('login'); }
Specify a Guard
add to auth
After the middleware is routed, you can also specify which guard to use for authentication. The specified guard corresponds to the configuration file config/auth.php
in guards
A key of the array:
public function __construct() { $this->middleware('auth:api'); }
If not specified, the default guard is web
, which is also configured in the configuration file:
'defaults' => [ 'guard' => 'web', 'passwords' => 'users', ],
Password confirmation
Sometimes, you may want to ask users to confirm their passwords before using application specific functions. For example, this is usually required when users modify bill settings or other sensitive information.
To achieve this function, Larravel provides password.confirm
Middleware. After adding this middleware to a route, when the user accesses the route, the user will be redirected to the confirmation password page first, and can continue only after entering the correct password:
Route::get('/settings/security', function () { //Users need to authenticate and confirm the password before accessing this route })->middleware(['auth', 'password.confirm']);
The password confirmation page is as follows:
After the user confirms the password successfully, it will be redirected to the previously accessed route /settings/security
。 By default, users can access the application within three hours after confirming the password password.confirm
The route of middleware does not need to confirm the password again. You can use the configuration item auth.password_timeout
Customize this duration:
'password_timeout' => 10800,
Login failure limit
If you use Larravel's own LoginController
Class, the built-in Illuminate\Foundation\Auth\ThrottlesLogins
Trait to limit the number of user login failures. By default, users cannot log in within one minute after several login failures. This restriction is based on the user name/email address+IP address as the unique key.
Manually authenticate users
Of course, you can also avoid using the authentication controller provided by Laravel. If you choose to remove these controllers, you need to directly use the Laravel authentication class to manage user authentication. Don't worry, it's easy!
We can Auth
Facade To access the authentication service, so we need to ensure that the Auth
Facade, next, let's see how to pass attempt
Method to implement login authentication:
<? php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; class LoginController extends Controller { /** *Process login authentication * * @return Response * @translator laravelacademy.org */ public function authenticate(Request $request) { $credentials = $request->only($this->username(), 'password'); if (Auth::attempt($credentials)) { //Certified return redirect()->intended('dashboard'); } } }
attempt
Method takes a key/value pair as the first parameter, and the value in the array is used to find the corresponding user from the data table. In the above example email
The value of is used as the query condition to get the corresponding user from the database. If the user is found, the password stored in the database after the hash operation will be compared with the password value passed through the hash operation. If the two hashed passwords match, an authentication session will be set for the user to indicate that the user successfully logged in. Interested students can see the underlying source code implementation logic. The corresponding source code is located in vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php
Medium:
/** * Attempt to authenticate a user using the given credentials. * * @param array $credentials * @param bool $remember * @return bool */ public function attempt(array $credentials = [], $remember = false) { $this->fireAttemptEvent($credentials, $remember); $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials); // If an implementation of UserInterface was returned, we'll ask the provider // to validate the user against the given credentials, and if they are in // fact valid we'll log the users into the application and return true. if ($this->hasValidCredentials($user, $credentials)) { $this->login($user, $remember); return true; } // If the authentication attempt fails we will fire an event so that the user // may be notified of any suspicious attempts to access their account from // an unrecognized user. A developer may listen to this event as needed. $this->fireFailedEvent($user, $credentials); return false; }
If the authentication is successful attempt
Method will return true
。 Otherwise, return false
。
On redirector intended
Method redirects the user to the URL that the user wants to access before logging in. If the target URL is invalid, the fallback URI will be passed to the method.
Specify additional conditions
If necessary, additional conditions can be added to the authentication query in addition to the user email and password. For example, we can verify the users marked as valid:
if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) { // The user is active, not suspended, and exists. }
The implementation principle here is that when querying user records, only the password fields in the array are excluded, and other fields will be filtered as one of the query criteria:
/** * Retrieve a user by the given credentials. * * @param array $credentials * @return \Illuminate\Contracts\Auth\Authenticatable|null */ public function retrieveByCredentials(array $credentials) { if (empty($credentials) || (count($credentials) === 1 && array_key_exists('password', $credentials))) { return; } // First we will add each credential element to the query as a where clause. // Then we can execute the query and, if we found a user, return it in a // Eloquent User "model" that will be utilized by the Guard instances. $query = $this->newModelQuery(); foreach ($credentials as $key => $value) { if (Str::contains($key, 'password')) { continue; } if (is_array($value) || $value instanceof Arrayable) { $query->whereIn($key, $value); } else { $query->where($key, $value); } } return $query->first(); }
Note: In these examples, it is not limited to the use of email
For login authentication, this is just a demonstration example. You can modify it to any other field in the database that can be used as "username".
Access the specified Guard instance
You can use Auth
Facade guard
Method specifies the guard instance you want to use. This mechanism allows you to implement completely independent user authentication for different authentication models or user tables in the same application.
This function can be used to facilitate isolated login for different types of users of different tables (users of different types of the same table can also theoretically). We just need to configure an independent guard for each table. For example, we except users
There is another one besides the table admins
The table is used to store the background administrator. To enable the administrator to log in separately, it can be configured in this way auth.php
Profile:
'guards' => [ ... 'admin' => [ 'driver' => 'session', 'provider' => 'admins', ] ], 'providers' => [ ... 'admins' => [ 'driver' => 'eloquent', 'model' => App\Admin::class, ], ],
Friendly prompt: the new model class for login authentication needs to inherit Illuminate\Foundation\Auth\User
Base class, or you will be unable to authenticate later.
Pass to guard
The guard name of the method corresponds to the configuration file auth.php
in guards
Configured admin
Key:
if (Auth::guard('admin')->attempt($credentials)) { // }
It should be noted that users authenticated in this way should also pass the matching guard when they need to pass the guard in subsequent operations, such as the above mentioned auth
For middleware, the corresponding invocation method should also be adjusted (the same is true for routing):
$this->middleware('auth:admin');
The same is true for getting users:
Auth::guard('admin')->user();
sign out
To exit the application, you can use Auth
Facade logout
Method, which will clear the authentication information in the user session:
Auth::logout();
Remember users
If you want to provide the "remember me" function in the application, you can pass a value of true
Boolean value of as the second parameter to attempt
Method (default is false
)In this way, the user login authentication status will remain until they manually exit. Of course, yours users
Table must contain remember_token
Field, which is used to store the "Remember Me" token.
if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) { // The user is being remembered... }
Note: If you are using your own LoginController
The controller should remember that the user logic has been implemented through the trait used by the controller.
If you are using the "Remember" user function, you can use the viaRemember
Method to determine whether the user authenticates through the "Remember Me" cookie:
if (Auth::viaRemember()) { // }
Other certification methods
Authenticate a user instance
If you need to log an existing user instance directly into the application, you can call Auth
Facade login
Method and pass in the user instance. The passed in instance must be Illuminate\Contracts\Auth\Authenticatable
contract Of course, Larave's built-in App\User
The model has implemented the interface:
Auth::login($user); //Log in and "remember" the given user Auth::login($user, true);
Of course, you can specify the guard instance you want to use:
Auth::guard('admin')->login($user);
Authenticate users through ID
To log in to the application through the user ID, you can use the loginUsingId
Method, which receives the primary key of the user you want to authenticate as a parameter:
Auth::loginUsingId(1); //Log in and "remember" the given user Auth::loginUsingId(1, true);
One time authenticated user
You can use once
The method only logs the user into the application in a single request without storing any sessions and cookies, which is useful in building stateless APIs:
if (Auth::once($credentials)) { // }
Basic authentication based on HTTP
HTTP Basic Authentication It can help users quickly achieve login authentication without setting a special login page. First, add auth.basic
middleware 。 This middleware comes with Larvel, so you don't need to define it yourself:
Route::get('profile', function() { //Only authenticated users can enter })->middleware('auth.basic');
After middleware is added to the route, when accessing the route in the browser, it will automatically prompt for authentication information. By default, auth.basic
Middleware uses the email
Field as the User Name.
Note to College: This basic authentication has no difference between the underlying implementation logic and normal login authentication except that there is no independent login form view.
Precautions on FastCGI
If you use PHP FastCGI, HTTP basic authentication will not work properly .htaccess
Add the following contents to the document:
RewriteCond %{HTTP:Authorization} ^(.+)$ RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
Stateless HTTP basic authentication
You can also use HTTP basic authentication without setting user ID cookies in the session, which is very useful in API authentication. To implement this function, you need to define a call onceBasic
Methodological middleware 。 If the method does not return any response, the request will continue:
<? php namespace Illuminate\Auth\Middleware; use Illuminate\Support\Facades\Auth; class AuthenticateOnceWithBasicAuth { /** *Process input request * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed * @translator laravelacademy.org */ public function handle($request, $next) { return Auth::onceBasic() ?: $ next($request); } }
Next AuthenticateOnceWithBasicAuth
Register to routing middleware And use it in routing:
Route::get('api/user', function() { //Only authenticated users can enter })->middleware('auth.basic.once');
sign out
If you want to manually exit the application, you can use Auth
On the facade logout
Method, which will clear the authentication information in the user session:
use Illuminate\Support\Facades\Auth; Auth::logout();
Invalidate sessions on other devices
Laravel also provides a mechanism to disable user sessions on other login devices besides the current device. This function is often used in scenarios where users modify or update passwords. After modifying passwords, other devices need to be re authenticated to improve security.
Before using this function, you need to ensure that Illuminate\Session\Middleware\AuthenticateSession
Middleware in app/Http/Kernel.php
Class web
The middleware group exists and is not commented:
'web' => [ // ... \Illuminate\Session\Middleware\AuthenticateSession::class, // ... ],
Then you can use Auth
On the facade logoutOtherDevices
The method implements "logout" on other devices. This method requires the user to provide a login password:
use Illuminate\Support\Facades\Auth; Auth::logoutOtherDevices($password);
When logoutOtherDevices
When the method is called, the user's session on other devices will fail completely, which means that the user will log out on the user interface.
Note: When used in combination AuthenticateSession
Middleware and login
The custom route name of the route must override the unauthenticated
Method so that the user can be redirected to the login page normally.
Add custom Guard driver
You can Auth
Facade extend
The method defines its own certified guard driver, which needs to be Service Provider Of boot
Method. Since Larravel already has a AuthServiceProvider
So we put the code into the service provider:
<? php namespace App\Providers; use App\Services\Auth\JwtGuard; use Illuminate\Support\Facades\Auth; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; class AuthServiceProvider extends ServiceProvider { /** *Register any application authentication/authorization service * * @return void */ public function boot() { $this->registerPolicies(); Auth::extend('jwt', function($app, $name, array $config) { //Return an Illuminate Contracts Auth Guard instance return new JwtGuard(Auth::createUserProvider($config['provider'])); }); } }
As you can see in the above example, the extend
The closure callback of the method needs to return Illuminate\Contracts\Auth\Guard
This interface contains some methods required for custom certification of the guard driver. After defining your own certified guard driver, you can enter the configuration file auth.php
Of guards
This new guard drive is used in the configuration:
'guards' => [ 'api' => [ 'driver' => 'jwt', 'provider' => 'users', ], ],
Closure Request Guard
The simplest way to implement a custom authentication system based on HTTP requests is to use Auth:viaRequest
Method, which allows you to quickly define the authentication process through a single closure.
First, we need to AuthServiceProvider
Of boot
Method Auth::viaRequest
, viaRequest
The method receives a guard name as the first parameter, which can be any string describing a custom guard. The second parameter passed to the method should be a closure, which receives HTTP requests and returns a user instance. If the authentication fails, it returns null
:
use App\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; /** * Register any application authentication / authorization services. * * @return void */ public function boot() { $this->registerPolicies(); Auth::viaRequest('custom-token', function ($request) { return User::where('token', $request->token)->first(); }); }
After defining a custom guard, you can click auth.php
Configuration item of configuration file guards
This guard is used in:
'guards' => [ 'api' => [ 'driver' => 'custom-token', ], ],
Add Custom User Provider
If you do not use a traditional relational database to store user information, you need to use your own authenticated user provider to extend Larave. We use Auth
On the facade provider
Method definitions customize the provider:
<? php namespace App\Providers; use App\Extensions\RiakUserProvider; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Auth; class AuthServiceProvider extends ServiceProvider { /** * Register any application authentication / authorization services. * * @return void */ public function boot() { $this->registerPolicies(); Auth::provider('riak', function ($app, array $config) { // Return an instance of Illuminate\Contracts\Auth\UserProvider... return new RiakUserProvider($app->make('riak.connection')); }); } }
adopt provider
After registering the user provider by using the config/auth.php
Switch to the new user provider in. First, define a provider
:
'providers' => [ 'users' => [ 'driver' => 'riak', ], ],
Then, you can guards
This provider is used in the configuration:
'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], ],
User Provider Contract
Illuminate\Contracts\Auth\UserProvider
The implementation is only responsible for obtaining from the persistent storage system Illuminate\Contracts\Auth\Authenticatable
Implementation, such as MySQL, Riak, etc. These two interfaces allow the Larravel authentication mechanism to continue to work regardless of how user data is stored or what classes are presented.
Let's see first Illuminate\Contracts\Auth\UserProvider
Covenants:
<? php namespace Illuminate\Contracts\Auth; interface UserProvider { public function retrieveById($identifier); public function retrieveByToken($identifier, $token); public function updateRememberToken(Authenticatable $user, $token); public function retrieveByCredentials(array $credentials); public function validateCredentials(Authenticatable $user, array $credentials); }
retrieveById
Method usually obtains a key representing the user, such as the self increasing ID in MySQL data. This method obtains and returns the ID matching Authenticatable
realization.
retrieveByToken
Functions are uniquely identified and stored in remember_token
The "Remember Me" token in the field gets the user. Like the previous method, this method also returns Authenticatable
realization.
updateRememberToken
Method uses the new $token
to update $user
Of remember_token
Field, the new token can be a newly generated token (select "Remember Me" to be successfully assigned when logging in) or null
(User exits).
retrieveByCredentials
Method gets the value passed to the Auth::attempt
The authentication information array of the method. This method then goes to the underlying persistent storage system to query the users matching the authentication information. Usually, this method runs a "where" condition( $credentials['username']
)Query for. Then the method returns Authenticatable
Implementation of. This method should not do any password verification and authentication.
validateCredentials
Method comparison given $user
and $credentials
To authenticate users. For example, this method compares $user->getAuthPassword()
String and longitude Hash::check
Processed $credentials['password']
。 This method returns a Boolean value based on whether the password is valid true
or false
。
Authenticable contract
Now that we have explored UserProvider
Let's take a look at each method on Authenticatable
。 Remember, the provider needs to retrieveById
and retrieveByCredentials
Return interface implementation in method:
<? php namespace Illuminate\Contracts\Auth; interface Authenticatable { public function getAuthIdentifierName(); public function getAuthIdentifier(); public function getAuthPassword(); public function getRememberToken(); public function setRememberToken($value); public function getRememberTokenName(); }
This interface is very simple, getAuthIdentifierName
Method returns the name of the user's primary key field, getAuthIdentifier
Method returns the user's "primary key", which will be a self increasing ID in the backend MySQL, getAuthPassword
Returns the hashed user password. This interface allows the authentication system to process any user class, whether you are using ORM or the storage abstraction layer. By default, Larravel app
Under directory User
Class implements this interface, so you can use this class as an implementation example.
event
Laravel supports triggering multiple event , you can EventServiceProvider
Listen for these events in:
/** *Applied event listener mapping * * @var array */ protected $listen = [ 'Illuminate\Auth\Events\Registered' => [ 'App\Listeners\LogRegisteredUser', ], 'Illuminate\Auth\Events\Attempting' => [ 'App\Listeners\LogAuthenticationAttempt', ], 'Illuminate\Auth\Events\Authenticated' => [ 'App\Listeners\LogAuthenticated', ], 'Illuminate\Auth\Events\Login' => [ 'App\Listeners\LogSuccessfulLogin', ], 'Illuminate\Auth\Events\Failed' => [ 'App\Listeners\LogFailedLogin', ], 'Illuminate\Auth\Events\Logout' => [ 'App\Listeners\LogSuccessfulLogout', ], 'Illuminate\Auth\Events\Lockout' => [ 'App\Listeners\LogLockout', ], 'Illuminate\Auth\Events\PasswordReset' => [ 'App\Listeners\LogPasswordReset', ], ];
Example Tutorial