Modify Laravel Auth to use salt and password for authentication.

Source: Internet
Author: User
Tags traits

Modify Laravel Auth to use salt and password for authentication.

Preface

This article describes how to modify Laravel Auth and use salt and password to authenticate users, let's take a look at the detailed introduction:

Laraval's user authentication system Auth is very powerful and easy to use, however, in Laravel's user authentication system, the user registration, login, and password retrieval modules use the password encryption and authentication algorithm bcrypt, many previous project user tables use the storage salt + password encryption string to record users' passwords, this brings a lot of resistance to rebuilding the previous project using the Laravel framework, however, I recently completed the Laravel Auth modification by searching for information on the Internet, viewing community forums, and viewing source code. I hope it will be helpful to others. Before starting the article, you need to explain that if the Laravel framework is applied to a new project, you do not need to modify Auth, the default bcrypt encryption algorithm is safer and more efficient than salt + password.

Modify user registration

First, the artisan command is used to enable verification in laravel.

php artisan make:auth

After the command is executed, there will be one more static method call in the routes file (location: app/Http/routes. php ).

Route::auth();

This Route is a Facade of Laravel (located in Illuminate \ Support \ Facades \ Route). The auth method called is defined in the Illuminate \ Routing \ Router class, as shown below, some auth-related routing rules are defined in the Auth method.

/** * Register the typical authentication routes for an application. * * @return void */public function auth(){ // Authentication Routes... $this->get('login', 'Auth\AuthController@showLoginForm'); $this->post('login', 'Auth\AuthController@login'); $this->get('logout', 'Auth\AuthController@logout'); // Registration Routes... $this->get('register', 'Auth\AuthController@showRegistrationForm'); $this->post('register', 'Auth\AuthController@register'); // Password Reset Routes... $this->get('password/reset/{token?}', 'Auth\PasswordController@showResetForm'); $this->post('password/email', 'Auth\PasswordController@sendResetLinkEmail'); $this->post('password/reset', 'Auth\PasswordController@reset');}

The routing rule shows that the requested Controller method at registration is the register Method of AuthController, which is defined in the traits of \ Illuminate \ Foundation \ Auth \ RegistersUsers, authController introduces this traits in the class definition.

/** * Handle a registration request for the application. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */public function register(Request $request){ $validator = $this->validator($request->all()); if ($validator->fails()) { $this->throwValidationException(  $request, $validator ); } Auth::guard($this->getGuard())->login($this->create($request->all())); return redirect($this->redirectPath());}

In the register method, the user input data in the request is verified first. You only need to define the validation rules for each input field in the validator method of AuthController.

protected function validator(array $data){ return Validator::make($data, [ 'name' => 'required|max:255', 'email' => 'required|email|max:255|unique:user', 'password' => 'required|size:40|confirmed', ]);}

After the verification is passed, Laravel will use the create method of AuthController to generate a new user, and then log on with the data of the new user.Auth::guard($this->getGuard())->login($this->create($request->all()));

Therefore, you only need to modify the create method of AuthController to customize the encryption method of user passwords generated during user registration.

For example:

/** * Create a new user instance after a valid registration. * * @param array $data * @return User */protected function create(array $data){ $salt = Str::random(6); return User::create([ 'nickname' => $data['name'], 'email' => $data['email'], 'password' => sha1($salt . $data['password']), 'register_time' => time(), 'register_ip' => ip2long(request()->ip()), 'salt' => $salt ]);}

Modify user logon

Before modifying logon, we need to first view the specific controller and method of the login request through the routing rules. In the auth method definition mentioned above, we can see

 $this->get('login', 'Auth\AuthController@showLoginForm'); $this->post('login', 'Auth\AuthController@login'); $this->get('logout', 'Auth\AuthController@logout');

The login verification operation is in the login method of the \ App \ Http \ Controllers \ Auth \ AuthController class. Open AuthController and find that Auth-related methods are introduced into the class through traits. use the traits to be introduced in the class, during compilation, PHP will copy the code in traits to the class. This is the feature introduced in PHP5.5. The specific application scenarios and usage are not described in detail here. InstituteUse AuthController @ loginThe method is actually defined in
\ Illuminate \ Foundation \ Auth \ AuthenticatesUsers

/** * Handle a login request to the application. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */public function login(Request $request){ $this->validateLogin($request); $throttles = $this->isUsingThrottlesLoginsTrait(); if ($throttles && $lockedOut = $this->hasTooManyLoginAttempts($request)) { $this->fireLockoutEvent($request); return $this->sendLockoutResponse($request); } $credentials = $this->getCredentials($request); if (Auth::guard($this->getGuard())->attempt($credentials, $request->has('remember'))) { return $this->handleUserWasAuthenticated($request, $throttles); } if ($throttles && ! $lockedOut) { $this->incrementLoginAttempts($request); } return $this->sendFailedLoginResponse($request);}

The main operation for Logon verification isAuth::guard($this->getGuard())->attempt($credentials, $request->has('remember'));This method is called,Auth::guard($this->getGuard()) \ Illuminate \ Auth \ SessionGuard is obtained. (For details, refer to the source code in the Facade \ Illuminate \ Auth \ AuthManager of Auth)

Let's take a look at how the attempt method is implemented in SessionGuard:

public function attempt(array $credentials = [], $remember = false, $login = true){ $this->fireAttemptEvent($credentials, $remember, $login); $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials); if ($this->hasValidCredentials($user, $credentials)) { if ($login) {  $this->login($user, $remember); } return true; } if ($login) { $this->fireFailedEvent($user, $credentials); } return false;}/** * Determine if the user matches the credentials. * * @param mixed $user * @param array $credentials * @return bool */protected function hasValidCredentials($user, $credentials){ return ! is_null($user) && $this->provider->validateCredentials($user, $credentials);}

RetrieveByCredentials extracts user data from the database using the passed fields. validateCredentials is the actual process used to verify that the password is correct.

Note that$this->providerThis provider is a provider that implements the \ Illuminate \ Contracts \ Auth \ UserProvider class. We can see that there are two userproviders under the Illuminate \ Auth directory: DatabaseUserProvider and EloquentUserProvider, but when we verify the password, we will verify it through that. Let's take a look at the auth configuration file.

'Providers '=> ['users' => ['drivers' => 'eloquent', 'model' => App \ User: class, // This is the Model used by the driver],],

The configuration here isdriver => eloquentIt is verified through retrieveByCredentials of EloquentUserProvider. This EloquentUserProvider is injected in the SessionGuard instance (specifically, how to read the auth configuration file, instantiate the corresponding provider and inject it into SessionGuard. Please refer to the source code of the createSessionDriver method in \ Illuminate \ Auth \ AuthManager)

Next, we will continue to check the implementation of the retrieveByCredentials and validateCredentials methods in EloquentUserProvider:

/** * Retrieve a user by the given credentials. * * @param array $credentials * @return \Illuminate\Contracts\Auth\Authenticatable|null */public function retrieveByCredentials(array $credentials){ if (empty($credentials)) { return; } $query = $this->createModel()->newQuery(); foreach ($credentials as $key => $value) { if (! Str::contains($key, 'password')) {  $query->where($key, $value); } } return $query->first();}/** * Validate a user against the given credentials. * * @param \Illuminate\Contracts\Auth\Authenticatable $user * @param array $credentials * @return bool */public function validateCredentials(UserContract $user, array $credentials){ $plain = $credentials['password']; return $this->hasher->check($plain, $user->getAuthPassword());}

The above two methods retrieveByCredentials uses a field other than the password to retrieve the user records from the Database User table. For example, you can use email to query the user records. Then, the validateCredentials method uses$this->haser->checkTo verify that the entered password is correct.

Now, we can see that it is obvious that we need to change the password for verification to implement validateCredentials ourselves, modify $ this-> hasher-> check to set our own password verification rules.

First, modify$user->getAuthPassword()Pass the salt and password of the table in the database to validateCredentials.
Modify App \ User. php and add the following code:

/*** The table associated to this model */protected $ table = 'user'; // The user table name is not specified in laravel.
/*** Disable Laravel to automatically manage timestamp columns */public $ timestamps = false;/*** overwrite the default getAuthPassword method in Laravel, return the user's password and salt field * @ return type */public function getAuthPassword () {return ['Password' => $ this-> attributes ['Password'], 'salt' => $ this-> attributes ['salt'];}

Then we create an implementation of our own UserProvider interface and put it in the Custom directory:

Create app/Foundation/Auth/AdminEloquentUserProvider. php

namespace App\Foundation\Auth;use Illuminate\Auth\EloquentUserProvider;use Illuminate\Contracts\Auth\Authenticatable;use Illuminate\Support\Str;class AdminEloquentUserProvider extends EloquentUserProvider{ /**  * Validate a user against the given credentials.  *  * @param \Illuminate\Contracts\Auth\Authenticatable $user  * @param array $credentials  */ public function validateCredentials(Authenticatable $user, array $credentials) {  $plain = $credentials['password'];  $authPassword = $user->getAuthPassword();  return sha1($authPassword['salt'] . $plain) == $authPassword['password']; }}

Finally, modify the auth configuration file so that Laravel can use the newly defined Provider for Auth verification,
Modify config/auth. php:

'providers' => [ 'users' => [  'driver' => 'admin-eloquent',  'model' => App\User::class, ]]

Modify app/Provider/AuthServiceProvider. php

public function boot(GateContract $gate){ $this->registerPolicies($gate); \Auth::provider('admin-eloquent', function ($app, $config) {  return New \App\Foundation\Auth\AdminEloquentUserProvider($app['hash'], $config['model']); });}

Auth: The provider method is used to register the Provider constructor. This constructor is a Closure. The specific code of the provider method is implemented in the AuthManager file.

public function provider($name, Closure $callback){ $this->customProviderCreators[$name] = $callback; return $this;}

The closure returns the AdminEloquentUserProvider object for Laravel Auth. After completing these changes, Laravel Auth adopts the custom salt + password method for user login verification.

Change Password Reset

Laravel's password reset workflow is:

  • Send an email with the password reset link to the email address of the user who needs to reset the password. The Link contains the user's email address and token.
  • Click the link in the email and enter the new password on the Reset Password page, laravel verifies email and token to confirm that the user initiates a password reset request and then updates the new password to the user's record in the data table.

In the first step, you need to configure the Laravel email function. In addition, you also need to create a new table password_resets in the database to store the user's email and the corresponding token.

CREATE TABLE `password_resets` ( `email` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `token` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `created_at` timestamp NOT NULL, KEY `password_resets_email_index` (`email`), KEY `password_resets_token_index` (`token`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

You can see from the address submitted by resetting the password form that the form uses post to submit the new password to/password/reset. Let's take a look at the auth-related route, determine the Controller method corresponding to/password/reset.

 $this->post('password/reset', 'Auth\PasswordController@reset');

The corresponding Controller method is the reset method of the \ App \ Http \ Controllers \ Auth \ PasswordController class. This method is actually defined in the traits of \ Illuminate \ Foundation \ Auth \ ResetsPasswords, passwordController introduces this traits

/** * Reset the given user's password. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */public function reset(Request $request){ $this->validate(  $request,  $this->getResetValidationRules(),  $this->getResetValidationMessages(),  $this->getResetValidationCustomAttributes() ); $credentials = $this->getResetCredentials($request); $broker = $this->getBroker(); $response = Password::broker($broker)->reset($credentials, function ($user, $password) {  $this->resetPassword($user, $password); }); switch ($response) {  case Password::PASSWORD_RESET:   return $this->getResetSuccessResponse($response);  default:   return $this->getResetFailureResponse($request, $response); }}

The method starts with validator, and then passes the new Password and a closure object in the program to the Password: broker ($ broker)-> reset (); method, this method is defined in the \ Illuminate \ Auth \ Passwords \ PasswordBroker class.

/** * Reset the password for the given token. * * @param array $credentials * @param \Closure $callback * @return mixed */public function reset(array $credentials, Closure $callback){ // If the responses from the validate method is not a user instance, we will // assume that it is a redirect and simply return it from this method and // the user is properly redirected having an error message on the post. $user = $this->validateReset($credentials); if (! $user instanceof CanResetPasswordContract) {  return $user; } $pass = $credentials['password']; // Once we have called this callback, we will remove this token row from the // table and return the response from this callback so the user gets sent // to the destination given by the developers from the callback return. call_user_func($callback, $user, $pass); $this->tokens->delete($credentials['token']); return static::PASSWORD_RESET;}

In the reset method of PasswordBroker, the program first authenticates the data submitted by the user, and then passes the password and user instance to the passed closure, update the new password to the user table in the closure call. In the closure, the program calls the resetPassword method of the PasswrodController class.

function ($user, $password) { $this->resetPassword($user, $password);});

PasswrodController resetPassword method definition

protected function resetPassword($user, $password){ $user->forceFill([  'password' => bcrypt($password),  'remember_token' => Str::random(60), ])->save(); Auth::guard($this->getGuard())->login($user);}

In this method, Laravel uses bcrypt to encrypt the password, so we need to change it to the salt + password method we need, we can override the resetPassword method in the PasswordController class to overwrite the method in traits.

/*** Override the resetPassword method in ResetsPasswords traits, and use the sha1 (salt + password) Encryption Method * Reset the given user's password. ** @ param \ Illuminate \ Contracts \ Auth \ CanResetPassword $ user * @ param string $ password * @ return void */protected function resetPassword ($ user, $ password) {$ salt = Str: random (6); $ user-> forceFill (['Password' => sha1 ($ salt. $ password), 'salt' => $ salt, 'Remember _ token' => Str: random (60),])-> save (); \ Auth :: guard ($ this-> getGuard ()-> login ($ user );}

Conclusion

Now the Laravel Auth customization is complete. The sha1 (salt + password) password encryption method is changed for registration, logon, and password resetting, all user-defined code completes the source code without modifying Laravel by defining sub-classes and rewriting methods of Laravel-related classes. This ensures good scalability and free migration of projects.

Note:The Laravel version used is 5.2.

Summary

The above is all the content of this article. I hope the content of this article will help you in your study or work. If you have any questions, please leave a message, thank you for your support.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.