《PHP实战:通过修改Laravel Auth使用salt和password进行认证用户详解》要点:
本文介绍了PHP实战:通过修改Laravel Auth使用salt和password进行认证用户详解,希望对您有用。如果有疑问,可以联系我们。
前言
PHP编程
本文主要给大家介绍了通过修改Laravel Auth用salt和password进行认证用户的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍:PHP编程
Laraval自带的用户认证系统Auth非常强大易用,不过在Laravel的用户认证系统中用户注册、登录、找回密码这些模块中用到密码加密和认证算法时使用的都是bcrypt,而很多之前做的项目用户表里都是采用存储salt + password加密字符串的方式来记录用户的密码的,这就给使用Laravel框架来重构之前的项目带来了很大的阻力,不过最近自己通过在网上找资料、看社区论坛、看源码等方式完成了对Laravel Auth的修改,在这里分享出来希望能对其他人有所帮助. 开篇之前需要再说明下如果是新项目应用Laravel框架,那么不需要对Auth进行任何修改,默认的bcrypt加密算法是比salt + password更安全更高效的加密算法.PHP编程
修改用户注册
PHP编程
首先,在laravel 里启用验证是用的artisan命令PHP编程
php artisan make:auth
执行完命令后在routes文件(位置:app/Http/routes.php)会多一条静态方法调用PHP编程
Route::auth();
这个Route是Laravel的一个Facade (位于Illuminate\Support\Facades\Route),调用的auth方法定义在Illuminate\Routing\Router类里,如下可以看到auth方法里就是定义了一些Auth相关的路由规则PHP编程
/** * 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.PHP编程
/** * 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方法里定义自己的每个输入字段的验证规则就可以PHP编程
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()));
PHP编程
所以我们要自定义用户注册时生成用户密码的加密方式只需要修改AuthController的create方法即可.
PHP编程
比如:PHP编程
/** * 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 ]); }
修改用户登录
PHP编程
修改登录前我们需要先通过路由规则看一下登录请求的具体控制器和方法,在上文提到的auth方法定义里可以看到PHP编程
$this->get('login','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里的PHP编程
/** * 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里的源码)PHP编程
看一下SessionGuard里attempt 方法是如何实现的:PHP编程
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是用来验证密码是否正确的实际过程.PHP编程
这里需要注意的是$this->provider
这个provider是一个实现了\Illuminate\Contracts\Auth\UserProvider类的provider,我们看到目录Illuminate\Auth下面有两个UserProvider的实现,分别为DatabaseUserProvider和EloquentUserProvider,但是我们验证密码的时候是通过那个来验证的呢,看一下auth的配置文件PHP编程
'providers' => [ 'users' => [ 'driver' => 'eloquent','model' => App\User::class,//这个是driver用的Model ],],
这里配置的是driver => eloquent
,那么就是通过EloquentUserProvider的retrieveByCredentials来验证的,这个EloquentUserProvider 是在SessionGuard实例化时被注入进来的,(具体是怎么通过读取auth配置文件,实例化相应的provider注入到SessionGuard里的请查阅\Illuminate\Auth\AuthManager 里createSessionDriver方法的源代码)PHP编程
接下来我们继续查看EloquentUserProvider中retrieveByCredentials和validateCredentials方法的实现:PHP编程
/** * 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()); }