Maison  >  Article  >  développement back-end  >  Explication détaillée de l'authentification des utilisateurs en modifiant Laravel Auth pour utiliser salt et mot de passe

Explication détaillée de l'authentification des utilisateurs en modifiant Laravel Auth pour utiliser salt et mot de passe

巴扎黑
巴扎黑original
2017-08-18 11:27:221651parcourir

Cet article vous présente principalement les informations pertinentes sur l'utilisation de salt et d'un mot de passe pour authentifier les utilisateurs en modifiant Laravel Auth. L'article le présente en détail à travers l'exemple de code. Il a une certaine valeur d'apprentissage de référence pour l'étude ou le travail de chacun. J'avais besoin d'amis, jetons un coup d'oeil ci-dessous.

Avant-propos

Cet article présente principalement le contenu pertinent de la modification de Laravel Auth pour utiliser le sel et le mot de passe pour authentifier les utilisateurs, le partager est ici pour référence et étude par chacun. Je ne dirai pas grand-chose ci-dessous. Jetons un coup d'œil à l'introduction détaillée :

Le système d'authentification des utilisateurs de Laraval, Auth, est très puissant et facile à utiliser. Le système d'authentification des utilisateurs de Laravel Les algorithmes de cryptage et d'authentification des mots de passe utilisés dans des modules tels que , la connexion et la récupération du mot de passe utilisent tous bcrypt, et de nombreux projets précédents utilisent la méthode de stockage des chaînes cryptées salt + password pour enregistrer les mots de passe des utilisateurs dans la table user , qui a. a apporté une grande résistance à l'utilisation du framework Laravel pour reconstruire des projets précédents. Cependant, j'ai récemment terminé la modification de Laravel Auth en recherchant des informations sur Internet, en lisant les forums communautaires et en regardant le code source, j'espère le partager ici. ça aide les autres. Avant de commencer, je dois expliquer que si le framework Laravel est utilisé dans un nouveau projet, il n'est pas nécessaire d'apporter des modifications à Auth. L'algorithme de chiffrement bcrypt par défaut est un algorithme de chiffrement plus sécurisé et plus efficace que salt + password.

Modifier l'enregistrement de l'utilisateur

Tout d'abord, activez la vérification dans Laravel à l'aide de la commande artisanale


php artisan make:auth

Après avoir exécuté la commande, il y aura un appel de méthode statique supplémentaire dans le fichier routes (emplacement : app/Http/routes.php)


Route::auth();

Cette route est une façade de Laravel (située dans IlluminateSupportFacadesRoute). La méthode auth appelée est définie dans la classe IlluminateRoutingRouter Comme vous pouvez le voir ci-dessous, la méthode auth définit certaines règles de routage liées à Auth

.

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

Vous pouvez voir dans les règles de routage que la méthode de contrôleur demandée lors de l'enregistrement est la méthode de registre d'AuthController. Cette méthode est définie dans les traits IlluminateFoundationAuthRegistersUsers introduit ces traits dans la définition de la classe. .


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

Dans la méthode d'enregistrement, les données saisies par l'utilisateur dans la demande seront d'abord vérifiées. Il vous suffit de définir vos propres règles de validation pour chaque champ de saisie. la méthode validateur d'AuthController. Vous pouvez


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',
 ]);
}

Ensuite, une fois la vérification réussie, Laravel utilisera la méthode create d'AuthController pour générer un nouvel utilisateur, et puis connectez-vous avec les données du nouvel utilisateurAuth::guard($this->getGuard())->login($this->create($request->all()));

Donc, si nous voulons personnaliser la méthode de cryptage pour générer les mots de passe des utilisateurs lors de l'enregistrement de l'utilisateur, il nous suffit de modifier la méthode de création d'AuthController.

Par exemple :


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

Modifier la connexion de l'utilisateur

Avant de modifier la connexion, nous devons d'abord examiner le contrôleur spécifique et la méthode de la demande de connexion via les règles de routage. Vous pouvez le voir dans la définition de la méthode d'authentification mentionnée ci-dessus


<.>

 $this->get(&#39;login&#39;, &#39;Auth\AuthController@showLoginForm&#39;);
 $this->post(&#39;login&#39;, &#39;Auth\AuthController@login&#39;);
 $this->get(&#39;logout&#39;, &#39;Auth\AuthController@logout&#39;);
L'opération de vérification de connexion se fait dans la méthode de connexion de la classe AppHttpControllersAuthAuthController. Ouvrez AuthController et constatez que les méthodes liées à Auth sont introduites dans la classe via les traits. Lors de la compilation, PHP copiera le code des traits dans la classe. les scénarios applicables et les utilisations des fonctionnalités introduites ne seront pas détaillés ici. La méthode

est en fait définie dans les traits 以AuthController@loginIlluminateFoundationAuthAuthenticatesUsers


/**
 * 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(&#39;remember&#39;))) {
 return $this->handleUserWasAuthenticated($request, $throttles);
 }

 if ($throttles && ! $lockedOut) {
 $this->incrementLoginAttempts($request);
 }

 return $this->sendFailedLoginResponse($request);
}
L'opération principale de vérification de connexion se trouve dans l'appel de méthode

In. progression, Auth::guard($this->getGuard())->attempt($credentials, $request->has('remember')); ce qui est obtenu est IlluminateAuthSessionGuard (pour plus de détails sur la façon de l'obtenir, voir le code source dans Auth Facade IlluminateAuthAuthManager) Auth::guard($this->getGuard())

Regardez comment la méthode de tentative dans SessionGuard est implémentée :


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 consiste à récupérer les données utilisateur de la base de données à l'aide des champs transmis, et validateCredentials est le processus réel utilisé pour vérifier si le mot de passe est correct.

Ce qu'il faut noter ici, c'est que

ce fournisseur est un fournisseur qui implémente la classe IlluminateContractsAuthUserProvider. Nous voyons qu'il existe deux implémentations de UserProvider sous le répertoire IlluminateAuth, à savoir DatabaseUserProvider et EloquentUserProvider, mais lorsque nous vérifions. le mot de passe Qui est utilisé pour vérifier ? Jetez un oeil au fichier de configuration d'authentification $this->provider


&#39;providers&#39; => [
 &#39;users&#39; => [
 &#39;driver&#39; => &#39;eloquent&#39;,
 &#39;model&#39; => App\User::class, //这个是driver用的Model
 ],
],

这里配置的是driver => eloquent , 那么就是通过EloquentUserProvider的retrieveByCredentials来验证的, 这个EloquentUserProvider 是在SessionGuard实例化时被注入进来的, (具体是怎么通过读取auth配置文件, 实例化相应的provider注入到SessionGuard里的请查阅\Illuminate\Auth\AuthManager 里createSessionDriver方法的源代码)

接下来我们继续查看EloquentUserProvider中retrieveByCredentials和validateCredentials方法的实现:


/**
 * 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, &#39;password&#39;)) {
  $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[&#39;password&#39;];

 return $this->hasher->check($plain, $user->getAuthPassword());
}

上面两个方法retrieveByCredentials用除了密码以外的字段从数据库用户表里取出用户记录,比如用email查询出用户记录,然后validateCredentials方法就是通过$this->haser->check来将输入的密码和哈希的密码进行比较来验证密码是否正确。

好了, 看到这里就很明显了, 我们需要改成自己的密码验证就是自己实现一下validateCredentials就可以了, 修改$this->hasher->check为我们自己的密码验证规则就可以了。

首先我们修改$user->getAuthPassword()把数据库中用户表的salt和password传递到validateCredentials中
修改App\User.php 添加如下代码


/**
 * The table associated to this model
 */
protected $table = &#39;user&#39;;//用户表名不是laravel约定的这里要指定一下


/**
 * 禁用Laravel自动管理timestamp列
 */
public $timestamps = false;

/**
 * 覆盖Laravel中默认的getAuthPassword方法, 返回用户的password和salt字段
 * @return type
 */
public function getAuthPassword()
{
 return [&#39;password&#39; => $this->attributes[&#39;password&#39;], &#39;salt&#39; => $this->attributes[&#39;salt&#39;]];
}

然后我们在建立一个自己的UserProvider接口的实现,放到自定义的目录中:

新建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[&#39;password&#39;];
  $authPassword = $user->getAuthPassword();

  return sha1($authPassword[&#39;salt&#39;] . $plain) == $authPassword[&#39;password&#39;];
 }
}

最后我们修改auth配置文件让Laravel在做Auth验证时使用我们刚定义的Provider,
修改config/auth.php:


&#39;providers&#39; => [
 &#39;users&#39; => [
  &#39;driver&#39; => &#39;admin-eloquent&#39;,
  &#39;model&#39; => App\User::class,
 ]
]

修改app/Provider/AuthServiceProvider.php


public function boot(GateContract $gate)
{
 $this->registerPolicies($gate);

 \Auth::provider(&#39;admin-eloquent&#39;, function ($app, $config) {
  return New \App\Foundation\Auth\AdminEloquentUserProvider($app[&#39;hash&#39;], $config[&#39;model&#39;]);
 });
}

Auth::provider方法是用来注册Provider构造器的,这个构造器是一个Closure,provider方法的具体代码实现在AuthManager文件里


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

 return $this;
}

闭包返回了AdminEloquentUserProvider对象供Laravel Auth使用,好了做完这些修改后Laravel的Auth在做用户登录验证的时候采用的就是自定义的salt + password的方式了。

修改重置密码

Laravel 的重置密码的工作流程是:

  • 向需要重置密码的用户的邮箱发送一封带有重置密码链接的邮件,链接中会包含用户的email地址和token。

  • 用户点击邮件中的链接在重置密码页面输入新的密码,Laravel通过验证email和token确认用户就是发起重置密码请求的用户后将新密码更新到用户在数据表的记录里。

第一步需要配置Laravel的email功能,此外还需要在数据库中创建一个新表password_resets来存储用户的email和对应的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;

通过重置密码表单的提交地址可以看到,表单把新的密码用post提交给了/password/reset,我们先来看一下auth相关的路由,确定/password/reset对应的控制器方法。


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

可以看到对应的控制器方法是\App\Http\Controllers\Auth\PasswordController类的reset方法,这个方法实际是定义在\Illuminate\Foundation\Auth\ResetsPasswords 这个traits里,PasswordController引入了这个traits


/**
 * Reset the given user&#39;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);
 }
}

方法开头先通过validator对输入进行验证,接下来在程序里传递把新密码和一个闭包对象传递给Password::broker($broker)->reset();方法,这个方法定义在\Illuminate\Auth\Passwords\PasswordBroker类里.


/**
 * 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[&#39;password&#39;];

 // 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[&#39;token&#39;]);

 return static::PASSWORD_RESET;
}

在PasswordBroker的reset方法里,程序会先对用户提交的数据做再一次的认证,然后把密码和用户实例传递给传递进来的闭包,在闭包调用里完成了将新密码更新到用户表的操作, 在闭包里程序调用了的PasswrodController类的resetPassword方法


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

PasswrodController类resetPassword方法的定义


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

 Auth::guard($this->getGuard())->login($user);
}

在这个方法里Laravel 用的是bcrypt 加密了密码, 那么要改成我们需要的salt + password的方式,我们在PasswordController类里重写resetPassword方法覆盖掉traits里的该方法就可以了。


/**
 * 覆盖ResetsPasswords traits里的resetPassword方法,改为用sha1(salt + password)的加密方式
 * Reset the given user&#39;s password.
 *
 * @param \Illuminate\Contracts\Auth\CanResetPassword $user
 * @param string $password
 * @return void
 */
protected function resetPassword($user, $password)
{
 $salt = Str::random(6);
 $user->forceFill([
  &#39;password&#39; => sha1($salt . $password),
  &#39;salt&#39; => $salt,
  &#39;remember_token&#39; => Str::random(60),
 ])->save();

 \Auth::guard($this->getGuard())->login($user);
}

结语

到这里对Laravel Auth的自定义就完成了,注册、登录和重置密码都改成了sha1(salt + password)的密码加密方式, 所有自定义代码都是通过定义Laravel相关类的子类和重写方法来完成没有修改Laravel的源码,这样既保持了良好的可扩展性也保证了项目能够自由迁移。

注:使用的Laravel版本为5.2

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn