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.

 User Table Structure

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:

 Auth related controller

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:

 User 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

 User login succeeded

To test the login function, you can exit the current user first and then visit the login page http://blog.test/login

 User Login Page

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:

 Authentication user information

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:

 Confirm Password Page

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


give the thumbs-up Cancel Like Collection Cancel Collection

<<Previous: Compile front-end resources using Mix

>>Next: User authorization