2016 年 12 月 23 日PHP Standard
添加自定义的 Guard
需要通过Auth门面的extend方法定义自己的认证guard,在App\Providers\AuthServiceProvider的boot方法中实现:
public function boot() { $this->registerPolicies(); Auth::extend('XXX', function($app, $name, array $config) { // 返回 Illuminate\Contracts\Auth\Guard 实例 $guard = new XXXGuard($name,Auth::createUserProvider($config['provider']),$this->app['session.store']); //事件 if (method_exists($guard, 'setDispatcher')) { $guard->setDispatcher($this->app['events']); } //请求 if (method_exists($guard, 'setRequest')) { $guard->setRequest($this->app->refresh('request', $guard, 'setRequest')); } return $guard; }); }
传递给 extend 方法的闭包回调需要返回 Illuminate\Contracts\Auth\Guard 的实现实例,该接口包含了自定义认证 guard 需要的一些方法:
<?php namespace App\Services\Auth; use Illuminate\Auth\Events\Authenticated; use Illuminate\Auth\Events\Failed; use Illuminate\Auth\Events\Login; use Illuminate\Auth\Events\Attempting; use Illuminate\Auth\Events\Logout; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Contracts\Auth\Guard; use Illuminate\Auth\GuardHelpers; use Illuminate\Contracts\Auth\UserProvider; use Illuminate\Contracts\Events\Dispatcher; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Session\SessionInterface; class XXXGuard implements Guard { use GuardHelpers; protected $session; protected $name; protected $loggedOut = false; protected $request; protected $events; public function __construct($name,UserProvider $provider,SessionInterface $session,Request $request = null) { $this->name = $name; $this->provider = $provider; $this->session = $session; $this->request = $request; } /** * Attempt to authenticate a user using the given credentials. * * @param array $credentials * @param bool $remember * @param bool $login * @return bool */ public function attempt(array $credentials = [], $remember = false, $login = true) { $this->fireAttemptEvent($credentials, $remember, $login); $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); } /** * Log a user into the application. * * @param \Illuminate\Contracts\Auth\Authenticatable $user * @param bool $remember * @return void */ public function login(Authenticatable $user, $remember = false) { $this->updateSession($user->getAuthIdentifier()); // If we have an event dispatcher instance set we will fire an event so that // any listeners will hook into the authentication events and run actions // based on the login and logout events fired from the guard instances. $this->fireLoginEvent($user, $remember); $this->setUser($user); } /** * Update the session with the given ID. * * @param string $id * @return void */ protected function updateSession($id) { $this->session->set($this->getName(), $id); $this->session->migrate(true); } /** * Get a unique identifier for the auth session value. * * @return string */ public function getName() { return 'login_'.$this->name.'_'.sha1(static::class); } /** * Get the currently authenticated user. * * @return \Illuminate\Contracts\Auth\Authenticatable|null */ public function user() { if ($this->loggedOut) { return; } // If we've already retrieved the user for the current request we can just // return it back immediately. We do not want to fetch the user data on // every call to this method because that would be tremendously slow. if (! is_null($this->user)) { return $this->user; } $id = $this->session->get($this->getName()); // First we will try to load the user using the identifier in the session if // one exists. Otherwise we will check for a "remember me" cookie in this // request, and if one exists, attempt to retrieve the user using that. $user = null; if (! is_null($id)) { if ($user = $this->provider->retrieveById($id)) { $this->fireAuthenticatedEvent($user); } } return $this->user = $user; } public function id() { if ($this->loggedOut) { return; } if ($this->user()) { return $this->user()->getAuthIdentifier(); } return $this->session->get($this->getName()); } /** * Validate a user's credentials. * * @param array $credentials * @return bool */ public function validate(array $credentials = []) { return $this->attempt($credentials, false, false); } /** * Fire the login event if the dispatcher is set. * * @param \Illuminate\Contracts\Auth\Authenticatable $user * @param bool $remember * @return void */ protected function fireLoginEvent($user, $remember = false) { if (isset($this->events)) { $this->events->fire(new Login($user, $remember)); } } /** * Fire the authenticated event if the dispatcher is set. * * @param \Illuminate\Contracts\Auth\Authenticatable $user * @return void */ protected function fireAuthenticatedEvent($user) { if (isset($this->events)) { $this->events->fire(new Authenticated($user)); } } /** * Fire the attempt event with the arguments. * * @param array $credentials * @param bool $remember * @param bool $login * @return void */ protected function fireAttemptEvent(array $credentials, $remember, $login) { if (isset($this->events)) { $this->events->fire(new Attempting( $credentials, $remember, $login )); } } /** * Fire the failed authentication attempt event with the given arguments. * * @param \Illuminate\Contracts\Auth\Authenticatable|null $user * @param array $credentials * @return void */ protected function fireFailedEvent($user, array $credentials) { if (isset($this->events)) { $this->events->fire(new Failed($user, $credentials)); } } /** * Log the user out of the application. * * @return void */ public function logout() { $user = $this->user(); // If we have an event dispatcher instance, we can fire off the logout event // so any further processing can be done. This allows the developer to be // listening for anytime a user signs out of this application manually. $this->clearUserDataFromStorage(); if (isset($this->events)) { $this->events->fire(new Logout($user)); } // Once we have fired the logout event we will clear the users out of memory // so they are no longer available as the user is no longer considered as // being signed into this application and should not be available here. $this->user = null; $this->loggedOut = true; } /** * Remove the user data from the session and cookies. * * @return void */ protected function clearUserDataFromStorage() { $this->session->remove($this->getName()); } /** * Get the event dispatcher instance. * * @return \Illuminate\Contracts\Events\Dispatcher */ public function getDispatcher() { return $this->events; } /** * Set the event dispatcher instance. * * @param \Illuminate\Contracts\Events\Dispatcher $events * @return void */ public function setDispatcher(Dispatcher $events) { $this->events = $events; } /** * Get the session store used by the guard. * * @return \Illuminate\Session\Store */ public function getSession() { return $this->session; } /** * Get the user provider used by the guard. * * @return \Illuminate\Contracts\Auth\UserProvider */ public function getProvider() { return $this->provider; } /** * Set the user provider used by the guard. * * @param \Illuminate\Contracts\Auth\UserProvider $provider * @return void */ public function setProvider(UserProvider $provider) { $this->provider = $provider; } /** * Return the currently cached user. * * @return \Illuminate\Contracts\Auth\Authenticatable|null */ public function getUser() { return $this->user; } /** * Set the current user. * * @param \Illuminate\Contracts\Auth\Authenticatable $user * @return $this */ public function setUser(Authenticatable $user) { $this->user = $user; $this->loggedOut = false; $this->fireAuthenticatedEvent($user); return $this; } public function setRequest(Request $request) { $this->request = $request; return $this; } }
定义好自己的认证 guard 之后,可以在配置文件 auth.php 的 guards 配置中使用话这个 guard:
'guards' => [ 'api' => [ 'driver' => 'XXX', 'provider' => 'users', ], ],
添加自定义用户提供者
同样在App\Providers\AuthServiceProvider的boot方法中实现:
public function boot() { $this->registerPolicies(); // Auth::provider('YYY', function($app, array $config) { // 返回 Illuminate\Contracts\Auth\UserProvider实例 return new YYYProvider($config['model']); }); }
定义提供者,返回Illuminate\Contracts\Auth\UserProvider的实现实例:
<?php namespace App\Services\Auth; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Contracts\Auth\UserProvider; use Illuminate\Support\Str; class YYYProvider implements UserProvider { protected $model; public function __construct($model) { $this->model = $model; } public function createModel() { $class = '\\'.ltrim($this->model, '\\'); return new $class; } /** * Retrieve a user by their unique identifier. * * @param mixed $identifier * @return \Illuminate\Contracts\Auth\Authenticatable|null */ public function retrieveById($identifier) { return $this->createModel()->newQuery()->find($identifier); } /** * Retrieve a user by their unique identifier and "remember me" token. * * @param mixed $identifier * @param string $token * @return \Illuminate\Contracts\Auth\Authenticatable|null */ public function retrieveByToken($identifier, $token) { //todo 根据token实现记住功能 return null; } /** * Update the "remember me" token for the given user in storage. * * @param \Illuminate\Contracts\Auth\Authenticatable $user * @param string $token * @return void */ public function updateRememberToken(Authenticatable $user, $token) { //code } /** * 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; } // First we will add each credential element to the query as a where clause. // Then we can execute the query and, if we found a user, return it in a // Eloquent User "model" that will be utilized by the Guard instances. $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(Authenticatable $user, array $credentials) { $plain = $credentials['password']; //具体验证密码有效性方法 return $user->Password==md5($plain); } /** * Gets the name of the Eloquent user model. * * @return string */ public function getModel() { return $this->model; } /** * Sets the name of the Eloquent user model. * * @param string $model * @return $this */ public function setModel($model) { $this->model = $model; return $this; } }
之后可以在配置文件 config/auth.php 中切换到新的用户提供者
'providers' => [ 'users' => [ 'driver' => 'YYY', ], ],
然后,就可以在你的 guards 配置中使用这个提供者。