搜索
首页后端开发php教程详解Laravel通过修改Auth使用salt和password认证

详解Laravel通过修改Auth使用salt和password认证

Jan 03, 2018 pm 03:55 PM
authlaravel修改

本文主要给大家介绍了关于通过修改Laravel Auth使用salt和password进行认证用户的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起看看吧。希望对大家有所帮助。

前言

本文主要给大家介绍了通过修改Laravel Auth用salt和password进行认证用户的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍:

Laraval自带的用户认证系统Auth非常强大易用,不过在Laravel的用户认证系统中用户注册、登录、找回密码这些模块中用到密码加密和认证算法时使用的都是bcrypt,而很多之前做的项目用户表里都是采用存储salt + password加密字符串的方式来记录用户的密码的,这就给使用Laravel框架来重构之前的项目带来了很大的阻力,不过最近自己通过在网上找资料、看社区论坛、看源码等方式完成了对Laravel Auth的修改,在这里分享出来希望能对其他人有所帮助。 开篇之前需要再说明下如果是新项目应用Laravel框架,那么不需要对Auth进行任何修改,默认的bcrypt加密算法是比salt + password更安全更高效的加密算法。

修改用户注册

首先,在laravel 里启用验证是用的artisan命令

php artisan make:auth

执行完命令后在routes文件(位置:app/Http/routes.php)会多一条静态方法调用

Route::auth();

这个Route是Laravel的一个Facade (位于Illuminate\Support\Facades\Route), 调用的auth方法定义在Illuminate\Routing\Router类里, 如下可以看到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方法, 该方法定义在\Illuminate\Foundation\Auth\RegistersUsers这个traits里,AuthController在类定义里引入了这个traits.

/**
 * 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方法里首先会对request里的用户输入数据进行验证,你只需要在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
 ]);
}

修改用户登录

修改登录前我们需要先通过路由规则看一下登录请求的具体控制器和方法,在上文提到的auth方法定义里可以看到

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

验证登录的操作是在\App\Http\Controllers\Auth\AuthController类的login方法里。打开AuthController发现Auth相关的方法都是通过性状(traits)引入到类内的,在类内use 要引入的traits,在编译时PHP就会把traits里的代码copy到类中,这是PHP5.5引入的特性具体适用场景和用途这里不细讲。 所以AuthController@login方法实际是定义在
\Illuminate\Foundation\Auth\AuthenticatesUsers这个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);
}

登录验证的主要操作是在Auth::guard($this->getGuard())->attempt($credentials, $request->has('remember'));这个方法调用中来进行的,Auth::guard($this->getGuard()) 获取到的是\Illuminate\Auth\SessionGuard (具体如何获取的看Auth这个Facade \Illuminate\Auth\AuthManager里的源码)

看一下SessionGuard里attempt 方法是如何实现的:

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这个provider是一个实现了\Illuminate\Contracts\Auth\UserProvider类的provider, 我们看到目录Illuminate\Auth下面有两个UserProvider的实现,分别为DatabaseUserProvider和EloquentUserProvider, 但是我们验证密码的时候是通过那个来验证的呢,看一下auth的配置文件

'providers' => [
 'users' => [
 'driver' => 'eloquent',
 'model' => 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, '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());
}

上面两个方法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 = '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

相关推荐:

Laravel 5.5的可相应接口如何使用?

Laravel Redis 多个进程同时取队列问题

laravel编写APP接口(API)

以上是详解Laravel通过修改Auth使用salt和password认证的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
优化PHP代码:减少内存使用和执行时间优化PHP代码:减少内存使用和执行时间May 10, 2025 am 12:04 AM

TOOPTIMIZEPHPCODEFORDUSEMEMORYUSAGEAGEAGEAGEAGEAGEANDEXECUTITIEM,关注台词:1)USEREEREFERESCENCENCINCOPYINSTEADOFCOPYINGINATATASTRUCTURESTROUCTURESTOREDUCEMORYCONSUMPTION.2)杠杆phphppphpphp'sbuilt intimpunctionslikearray_mapforfunctionslikearray_mapforfforfforfforfasterapasterexecution.3)

PHP电子邮件:分步发送指南PHP电子邮件:分步发送指南May 09, 2025 am 12:14 AM

phpisusedforsendendemailsduetoitsignegrationwithservermailservicesand andexternalsmtpproviders,自动化notifications andMarketingCampaigns.1)设置设置yourphpenvironcormentswironmentswithaweberswithawebserverserverserverandphp,确保themailfunctionisenabled.2)useabasicscruct

如何通过PHP发送电子邮件:示例和代码如何通过PHP发送电子邮件:示例和代码May 09, 2025 am 12:13 AM

发送电子邮件的最佳方法是使用PHPMailer库。1)使用mail()函数简单但不可靠,可能导致邮件进入垃圾邮件或无法送达。2)PHPMailer提供更好的控制和可靠性,支持HTML邮件、附件和SMTP认证。3)确保正确配置SMTP设置并使用加密(如STARTTLS或SSL/TLS)以增强安全性。4)对于大量邮件,考虑使用邮件队列系统来优化性能。

高级PHP电子邮件:自定义标题和功能高级PHP电子邮件:自定义标题和功能May 09, 2025 am 12:13 AM

CustomHeadersheadersandAdvancedFeaturesInphpeMailenHanceFunctionalityAndreliability.1)CustomHeadersheadersheadersaddmetadatatatatataatafortrackingandCategorization.2)htmlemailsallowformattingandttinganditive.3)attachmentscanmentscanmentscanbesmentscanbestmentscanbesentscanbesentingslibrarieslibrarieslibrariesliblarikelikephpmailer.4)smtppapapairatienticationaltication enterticationallimpr

使用PHP和SMTP发送电子邮件的指南使用PHP和SMTP发送电子邮件的指南May 09, 2025 am 12:06 AM

使用PHP和SMTP发送邮件可以通过PHPMailer库实现。1)安装并配置PHPMailer,2)设置SMTP服务器细节,3)定义邮件内容,4)发送邮件并处理错误。使用此方法可以确保邮件的可靠性和安全性。

使用PHP发送电子邮件的最佳方法是什么?使用PHP发送电子邮件的最佳方法是什么?May 08, 2025 am 12:21 AM

ThebestapproachforsendingemailsinPHPisusingthePHPMailerlibraryduetoitsreliability,featurerichness,andeaseofuse.PHPMailersupportsSMTP,providesdetailederrorhandling,allowssendingHTMLandplaintextemails,supportsattachments,andenhancessecurity.Foroptimalu

PHP中依赖注入的最佳实践PHP中依赖注入的最佳实践May 08, 2025 am 12:21 AM

使用依赖注入(DI)的原因是它促进了代码的松耦合、可测试性和可维护性。1)使用构造函数注入依赖,2)避免使用服务定位器,3)利用依赖注入容器管理依赖,4)通过注入依赖提高测试性,5)避免过度注入依赖,6)考虑DI对性能的影响。

PHP性能调整技巧和技巧PHP性能调整技巧和技巧May 08, 2025 am 12:20 AM

phperformancetuningiscialbecapeitenhancesspeedandeffice,whatevitalforwebapplications.1)cachingwithapcureduccureducesdatabaseloadprovesrovesponsemetimes.2)优化

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

mPDF

mPDF

mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

EditPlus 中文破解版

EditPlus 中文破解版

体积小,语法高亮,不支持代码提示功能

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )专业的PHP集成开发工具

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器