config/auth.php
return [
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',//指向llluminate/Auth/SessionGuard.php
'provider' => 'users',
],
'api' => [
'driver' => 'token', //指向llluminate/Auth/TokenGuard.php
'provider' => 'wx_user',
'hash' => false,//TokenGuard.php文件类属性
'input' => 'token', //TokenGuard.php文件类属性
'storage_key' => 'token', //TokenGuard.php文件类属性
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'wx_user' => [
'driver' => 'eloquent',
'model' =>App\Model\UserModel::class,
]
],
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
'throttle' => 60,
],
],
'password_timeout' => 10800,
];
Auth::check() //判断用户是否登陆状态,如果为false,直接重定向到 /login,为什么用Redirect::guest()而不用Redirect::to()呢,通过api手册可以查到:Redirect::guest() 在重定向时会将当前url保存到session中,这样可以在登陆以后,使用Redirect::intended()方法跳转到之前的页面继续业务。
Auth::attempt(array('email' => $email, 'password' => $password)) //attempt 接收一个数组来作为参数1,该参的值将用于寻找数据库中的用户数据。如demo用户将用 email 值在数据库中进行查找,如果用户被找到,在将 password 值进行哈希加密并与数据库中已加密过的密码匹配,如果匹配到,则创建个通过认证的会话给用户。会话获取到后,即视为用户登录成功。当用户身份认证成功 attempt 方法会返回 true,反之则返回 false。
//Auth只是帮助实现了验证逻辑,如果匹配成功会自动帮我们写入session,这样下次Auth::check()的时候就通过了。
//Redirect::intended(‘/’)方法是跳转到之前页面,如果像上面那样使用了Redirect::guest()方法,那么intended这里就会跳转到那时候的url,而它的参数只是一个默认值,再没有记录历史url的时候会跳转到’/’。
//Auth还有些其他方法,如 Auth::basic() 可以实现http basic认证。
# config/app.php
'aliases' => [
'App' => Illuminate\Support\Facades\App::class,
'Artisan' => Illuminate\Support\Facades\Artisan::class,
'Auth' => Illuminate\Support\Facades\Auth::class,
看到调用Auth其实是调用了 Illuminate\Support\Facades\Auth::class
,打开这个类文件
class Auth extends Facade
{
protected static function getFacadeAccessor()
{
return 'auth';
}
// ...
}
看到,Auth是通过Facade动态绑定的,绑定到哪里呢,进一步寻找发现
在 vendor/laravel/framework/src/Illuminate/AuthServiceProvider
中
class AuthServiceProvider extends ServiceProvider
{
/**
* Register the authenticator services.
*
* @return void
*/
protected function registerAuthenticator()
{
$this->app->singleton('auth', function ($app) {
$app['auth.loaded'] = true;
return new AuthManager($app);
});
$this->app->singleton('auth.driver', function ($app) {
return $app['auth']->guard();
});
}
}
默认的Auth
绑定了AuthManager
,打开AuthManager
文件
namespace Illuminate\Auth;
use Closure;
use InvalidArgumentException;
use Illuminate\Contracts\Auth\Factory as FactoryContract;
class AuthManager implements FactoryContract
{
use CreatesUserProviders;
protected $app;
protected $guards = [];
public function guard($name = null)
{
$name = $name ?: $this->getDefaultDriver();
return isset($this->guards[$name])
? $this->guards[$name]
: $this->guards[$name] = $this->resolve($name);
}
public function getDefaultDriver()
{
return $this->app['config']['auth.defaults.guard'];
}
public function __call($method, $parameters)
{
return $this->guard()->{$method}(...$parameters);
}
}
并没找到attempt方法,但有个__call
魔术方法,那肯定是他里面,为快速找到他,直接用 dd(get_class($this->guard()));
,真正的attempt究竟被谁调用了呢?打印了SessionGuard,继续找下去 Illuminate\Auth\SessionGuard
,打开该类,发现终于发现了attempt的实现
class SessionGuard implements StatefulGuard, SupportsBasicAuth
{
use GuardHelpers, Macroable;
public function attempt(array $credentials = [], $remember = false)
{
$this->fireAttemptEvent($credentials, $remember);
$this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
if ($this->hasValidCredentials($user, $credentials)) {
$this->login($user, $remember);
return true;
}
$this->fireFailedEvent($user, $credentials);
return false;
}
这就是attempt
实现,通过 $this->provider->retrieveByCredentials($credentials)
获取用户信息,并验证,如果成功则登录,并返回true,所以我们真正做的密码验证肯定在retrieveByCredentials
这个方法里面 Laravel 默认提供了 UserProvider
为 EloquentUserProvider
打开改方法
class EloquentUserProvider implements UserProvider
{
protected $hasher;
protected $model;
public function __construct(HasherContract $hasher, $model)
{
$this->model = $model;
$this->hasher = $hasher;
}
public function validateCredentials(UserContract $user, array $credentials)
{
$plain = $credentials['password'];
return $this->hasher->check($plain, $user->getAuthPassword());
}
public function setHasher(HasherContract $hasher)
{
$this->hasher = $hasher;
return $this;
}
}
所以这里的hasher就是系统默认的BcryptHasher了,我们修改他直接注入自己的haser。ok,了解思路了,开始搞它
namespace App\Helpers\Hasher;
use Illuminate\Contracts\Hashing\Hasher;
class MD5Hasher implements Hasher
{
public function check($value, $hashedValue, array $options = [])
{
return $this->make($value) === $hashedValue;
}
public function needsRehash($hashedValue, array $options = [])
{
return false;
}
public function make($value, array $options = [])
{
$value = env('SALT', '').$value;
return md5($value); //这里写你自定义的加密方法
}
}
创建MD5HashServiceProvider
php artisan make:provider MD5HashServiceProvider
添加如下方法
namespace App\Providers;
use App\Helpers\Hasher\MD5Hasher;
use Illuminate\Support\ServiceProvider;
class MD5HashServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* @return void
*/
public function boot()
{
$this->app->singleton('hash', function () {
return new MD5Hasher;
});
}
/**
* Register the application services.
*
* @return void
*/
public function register()
{
//
}
public function provides()
{
return ['hash'];
}
}
然后在config/app.php
的providers
中,将 Illuminate\Hashing\HashServiceProvider::class,
替换为 \App\Providers\MD5HashServiceProvider::class,
OK,大功告成
Auth::guard("api")->user();
Auth::guard("api")->check();
Auth::guard("api")->attempt();
Auth::guard("api")->id();