Detailed laravel by modifying auth using Salt and password certification

Source: Internet
Author: User
Tags sha1 traits
This article mainly introduces to you by modifying the Laravel auth use salt and password to authenticate the user's relevant information, the text through the example code introduced in very detailed, to everyone's study or work has a certain reference learning value, need to have friends below to see it. We hope to help you.





Objective


This article mainly introduces to you by modifying the Laravel auth with salt and password to authenticate the user's relevant content, share out for everyone to reference the study, the following words do not say, come together to see the detailed introduction:



Laraval user authentication system Auth is very powerful and easy to use, but in Laravel user authentication system, user registration, login, retrieve password in these modules using the password encryption and authentication algorithm used is bcrypt, and many of the previous Project user table is to use a storage salt + Password encrypt the string to record the user's password, which gives the use of the Laravel framework to reconstruct the project before the great resistance, but recently himself through the Internet to find information, see community forums, see the source and other ways to complete the laravel auth modification, Share it here and hope it will help others. Before you get to the beginning, you need to explain that if you apply the Laravel framework to a new project, you do not need to make any modifications to auth, and the default Bcrypt encryption algorithm is more secure and efficient than the SALT + password encryption algorithm.



Modify User Registration


First, the artisan command to enable authentication in Laravel




PHP Artisan Make:auth


One more static method call in the routes file (location: app/http/routes.php) after executing the command




Route::auth ();


This Route is a facade (located in Illuminate\support\facades\route) of Laravel, and the Auth method called is defined in the Illuminate\routing\router class, As you can see in the Auth method, you define some auth-related routing rules.






/**
 * 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');
}


By routing rules you can see that the Controller method requested at registration is the Authcontroller register method, which is defined in the \illuminate\foundation\auth\registersusers traits, Authcontroller introduced 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());
}


The Register method first validates the user input data in the request, and you only need to define your own validation rules for each input field in the Authcontroller validator method.






/**
 * 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());
}


Then after the verification pass, Laravel will drop the Authcontroller create method to generate a new user, and then take the new user's data to log inAuth::guard($this->getGuard())->login($this->create($request->all()));



So we're going to customize the encryption method of generating user password when user registers only need to modify Authcontroller's Create method.



Like what:






/**
 * 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 Login


Before modifying the login we need to see the specific controller and method of the login request through the routing rules, which can be seen in the Auth method definition mentioned above.






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


Verify that the login operation is in the login method of the \app\http\controllers\auth\authcontroller class. Open Authcontroller found Auth related methods are introduced into the class through the traits (traits), in the class use to introduce the traits, at compile time PHP will copy the traits code into the class, This is the specific application scenario and use of the features introduced by PHP5.5 is not detailed here. The以AuthController@loginmethod is actually defined in the
\illuminate\foundation\auth\authenticatesusers in this traits.






/**
 * 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 of login verification isAuth::guard($this->getGuard())->attempt($credentials, $request->has('remember'));done in this method call,Auth::guard($this->getGuard())get to the \illuminate\auth\sessionguard (how to get the see Auth this facade \illuminate\auth\ AuthManager in the source code)



Take a look at how the Sessionguard attempt method is implemented:






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 is the actual process of extracting the user data from the database with the passed in field, and validatecredentials is used to verify that the password is correct.



It is important to note that$this->providerthis provider is a provider that implements the \illuminate\contracts\auth\userprovider class, and we see the directory illuminate\ Auth below there are two implementations of Userprovider, respectively, Databaseuserprovider and Eloquentuserprovider, but we verify the password by that to verify, look at the auth configuration file




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


The configuration here isdriver => eloquent, then, through Eloquentuserprovider's retrievebycredentials to verify that this Eloquentuserprovider is injected in the Sessionguard instantiation, (specifically how to read the Auth configuration file, instantiate the corresponding provider injected into the Sessionguard, please refer to the \illuminate\auth\authmanager The source code of the Createsessiondriver method)



Next we continue to look at 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 use a field other than the password to remove user records from the database user's table, such as using email to query the user records, and then Validatecredentials method is through$this->haser->checkTo compare the password entered and the hashed password to verify that the password is correct.



Well, see here is very obvious, we need to change to their own password verification is to implement a validatecredentials on their own, modify the $this->hasher->check for our own password validation rules can be.



First we modify the$user->getAuthPassword()salt and password of the user table in the database to Validatecredentials
Modify app\user.php Add the following code




/ **
  * The table associated to this model
  * /
protected $ table = 'user'; // user table name is not laravel convention here to specify


/ **
  * Disable Laravel to automatically manage timestamp columns
  * /
public $ timestamps = false;

/ **
  * Override the default getAuthPassword method in Laravel, returning the user's password and salt fields
  * @return type
  * /
public function getAuthPassword ()
{
  return ['password' => $ this-> attributes ['password'], 'salt' => $ this-> attributes ['salt']];
}


We then set up an implementation of our own Userprovider interface and put it in a custom directory:



New 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, we modify the Auth configuration file to let Laravel use the provider we just defined when doing 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:p Rovider method is used to register the provider constructor, which is a closure,provider method of the specific code implemented in the AuthManager file






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

 return $this;
}


The closure returned the Admineloquentuserprovider object for Laravel auth use, OK, after these modifications Laravel auth in the user login verification is the use of custom salt + password way.



Modify Reset Password


The workflow for resetting passwords for Laravel is:


    • Send a message with the Reset password link to the mailbox of the user who needs to reset the password, and the link will contain the user's email address and token.

    • The user clicks on the link in the message to enter a new password on the Reset Password page, laravel the new password to the user's record in the data table by verifying that the user is the user who initiated the Reset password request after authenticating the email and token.


The first step is to configure the Laravel email feature, and also to create a new table in the database password_resets to store the user's email and corresponding tokens






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;


By resetting the submission address of the password form, you can see that the form submits the new password to the/password/reset by post, and we first look at the auth-related routes to determine the/password/reset corresponding controller method.




$this->post (' Password/reset ', ' auth\passwordcontroller@reset ');


You can see that the corresponding controller method is the reset method of the \app\http\controllers\auth\passwordcontroller class, and this method is actually defined in the \illuminate\foundation\auth\ Resetspasswords This traits, Passwordcontroller introduced 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 first validates the input by validator, then passes the new password and a closure object to the Password::broker ($broker)->reset () in the program, which is defined in the \illuminate\ In the 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 Passwordbroker reset method, the program will first authenticate the user's submitted data, then pass the password and user instance to the closed packet, and complete the operation of updating the new password to the user table in the closure call. The ResetPassword method of the Passwrodcontroller class that the program called in the closure






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


Definition of Passwrodcontroller class ResetPassword method






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

 Auth::gu


In this method laravel use is bcrypt encrypted password, then to change to We need salt + password way, We can override this method in the Passwordcontroller class by overriding the ResetPassword method in the traits.




/ **
  * Override the resetPassword method in ResetsPasswords traits and use sha1 (salt + password) encryption
  * 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



The custom of Laravel Auth is completed, and the registration, login and reset password are changed to SHA1 (salt + password) password encryption method. All custom code is done by defining the subclasses of the Laravel related classes and overriding methods to complete the source code without modifying the Laravel, thus maintaining good scalability and ensuring that the project is free to migrate.



Note: The laravel version used is 5.2



Related recommendations:



How can I use the Laravel 5.5 interface?



Laravel Redis multiple processes simultaneously fetching queue issues



Laravel writing the App interface (API)


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.