ホームページ > バックエンド開発 > PHPチュートリアル > 認証では、ユーザー認証の例にソルトとパスワードを使用します。

認証では、ユーザー認証の例にソルトとパスワードを使用します。

小云云
リリース: 2023-03-19 16:48:01
オリジナル
2328 人が閲覧しました

この記事では、Laravel Auth を変更してユーザーを認証するためのソルトとパスワードに関する関連情報を中心に、サンプルコードを通じて詳しく紹介していますので、皆さんの学習や仕事に役立つことを願っています。みんな。

Laraval 独自のユーザー認証システム Auth は非常に強力で使いやすいですが、Laravel のユーザー認証システムでは、ユーザー登録、ログイン、パスワード取得などのモジュールで使用されるパスワード暗号化および認証アルゴリズムは bcrypt を使用しています。以前のプロジェクトのユーザーテーブルでは、ユーザーパスワードを記録するためにソルト+パスワード暗号化文字列を保存する方法が使用されていましたが、これは以前のプロジェクトを再構築するためにLaravelフレームワークを使用することに大きな抵抗をもたらしましたが、最近、Laravel Authの変更を完了しました。オンラインで情報を探したり、コミュニティ フォーラムを読んだり、ソース コードを読んだりして、ここで共有することで他の人に役立つことを願っています。 始める前に、Laravel フレームワークを新しいプロジェクトで使用する場合、Auth に変更を加える必要がないことを説明する必要があります。デフォルトの bcrypt 暗号化アルゴリズムは、salt + パスワードよりも安全で効率的な暗号化アルゴリズムです。

ユーザー登録を変更します

まず、artisanコマンドを使用してlaravelで検証を有効にします

php artisan make:auth
ログイン後にコピー

コマンドを実行すると、ルートファイル(場所: app/Http/routes.php)に追加の静的メソッド呼び出しが追加されます

Route::auth();
ログイン後にコピー

この Route は Laravel の Facade (IlluminateSupportFacadesRoute にあります) です。呼び出される auth メソッドは IlluminateRoutingRouter クラスで定義されています。以下に示すように、auth メソッドはいくつかの 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');
}
ログイン後にコピー

。ルーティング ルールを介したリクエスト コントローラー メソッドは、AuthController の register メソッドです。このメソッドは、IlluminateFoundationAuthRegistersUsers トレイトで定義されています。AuthController は、このトレイトをクラス定義に導入します。

/**
 * 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());
}
ログイン後にコピー

register メソッドでは、リクエスト内のユーザー入力データが最初に追加されます。 AuthController の validator メソッドで各入力フィールドに独自の検証ルールを定義する必要があります

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',
 ]);
}
ログイン後にコピー

そして、検証が完了すると、Laravel は AuthController の create メソッドを使用して新しいユーザーを生成します。そして新しいユーザーのデータを取得します ログインに移動します Auth::guard($this->getGuard())->login($this->create($request->all()));

ユーザー登録時の世代をカスタマイズする必要があります。ユーザーパスワードの暗号化方式はAuthControllerのcreateメソッドを変更するだけです。

例:

/**
 * 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
 ]);
}
ログイン後にコピー

ユーザーログインを変更する

ログインを変更する前に、まずルーティングルールを通じてログインリクエストの特定のコントローラーとメソッドを確認する必要があります。これは、上記の認証メソッド定義で確認できます

 $this->get('login', 'Auth\AuthController@showLoginForm');
 $this->post('login', 'Auth\AuthController@login');
 $this->get('logout', 'Auth\AuthController@logout');
ログイン後にコピー

ログイン認証の操作はAppHttpControllersAuthAuthControllerクラスのloginメソッド内で行われます。 AuthController を開き、Auth 関連のメソッドがクラスに導入されることを確認します。コンパイル中に、PHP はそのトレイト内のコードをクラスにコピーします。適用可能なシナリオと、導入された機能の使用方法については、ここでは詳しく説明しません。 したがって、AuthController@login メソッドは実際には
IlluminateFoundationAuthAuthenticatesUsers

/**
 * 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);
}
ログイン後にコピー
のトレイトで定義されています。ログイン検証の主な操作は Auth::guard($this->getGuard())->attempt($credentials, $request- >has('remember')); このメソッドが呼び出され、Auth::guard($this->getGuard()) が IlluminateAuthSessionGuard を取得します (取得方法の詳細については、Auth Facade IlluminateAuthAuthManager のソース コードを参照してください) )

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 は渡されたフィールドを使用してデータベースからユーザー データを取得し、validateCredentials はパスワードが正しいかどうかを確認するために使用される実際のプロセスです。

ここで注意する必要があるのは、$this->provider が IlluminateContractsAuthUserProvider クラスを実装するプロバイダーであるということです。IlluminateAuth ディレクトリの下に、DatabaseUserProvider と EloquentUserProvider という 2 つの UserProvider 実装があることがわかりますが、パスワードを確認すると、それを確認するには、認証設定ファイルを見てください

'providers' => [
 'users' => [
 'driver' => 'eloquent',
 'model' => App\User::class, //这个是driver用的Model
 ],
],
ログイン後にコピー
ここで設定されているのは driver => eloquent であり、EloquentUserProvider のretrieveByCredentials を通じて確認されます。この EloquentUserProvider は、SessionGuard がインスタンス化されるときに挿入されます。認証構成ファイルを読み取り、対応するプロバイダーをインスタンス化し、SessionGuard に挿入します。IlluminateAuthAuthManager の 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, '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());
}
ログイン後にコピー

上記の 2 つのメソッドretrieveByCredentials は、パスワード以外のフィールドを使用してデータベース ユーザー テーブルからユーザー レコードを取得します (電子メールを使用してユーザー レコードをクエリするなど)。次に、validateCredentials メソッドは $this->haser->check を使用して入力された情報を結合します。パスワードとハッシュ パスワードを比較して、パスワードが正しいかどうかを確認します。

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

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

/**
 * The table associated to this model
 */
protected $table = 'user';//用户表名不是laravel约定的这里要指定一下
ログイン後にコピー
/**
 * 禁用Laravel自动管理timestamp列
 */
public $timestamps = false;

/**
 * 覆盖Laravel中默认的getAuthPassword方法, 返回用户的password和salt字段
 * @return type
 */
public function getAuthPassword()
{
 return ['password' => $this->attributes['password'], 'salt' => $this->attributes['salt']];
}
ログイン後にコピー

然后我们在建立一个自己的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['password'];
  $authPassword = $user->getAuthPassword();

  return sha1($authPassword['salt'] . $plain) == $authPassword['password'];
 }
}
ログイン後にコピー

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

'providers' => [
 'users' => [
  'driver' => 'admin-eloquent',
  'model' => App\User::class,
 ]
]
ログイン後にコピー

修改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::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('password/reset', 'Auth\PasswordController@reset');
ログイン後にコピー

可以看到对应的控制器方法是\App\Http\Controllers\Auth\PasswordController类的reset方法,这个方法实际是定义在\Illuminate\Foundation\Auth\ResetsPasswords 这个traits里,PasswordController引入了这个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);
 }
}
ログイン後にコピー

方法开头先通过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['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;
}
ログイン後にコピー

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

function ($user, $password) {
 $this->resetPassword($user, $password);
});
ログイン後にコピー

PasswrodController类resetPassword方法的定义

protected function resetPassword($user, $password)
{
 $user->forceFill([
  'password' => bcrypt($password),
  'remember_token' => 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'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);
}
ログイン後にコピー

结语

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

注:使用的Laravel版本为5.2

相关推荐:

Laravel5.5中利用Passport实现Auth认证的方法

详解Laravel框架中的Auth模块

自己写的适配tp5的Auth验证demo

以上が認証では、ユーザー認証の例にソルトとパスワードを使用します。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート