本次项目的后台使用 iview-admin 搭建,前后端是分离的,需要给后台管理也写一套接口(看了几篇资料说可以把iview-admin 放到 laravel 项目里跑,但没有成功,卒),于是想用 passport 来做后台接口的认证。谁知过程一波三折,折腾了两天,算是摸清了坑也爬起来了。坑了一次不能再被坑第二次,也不希望后面的人接着被坑,于是就有了这篇文章,给大家介绍一下整体的流程和避坑操作。
安装 项目是 Laravel 5.5 的,直接使用 composer require laravel/passport
安装可能会出现报错。如果报错的话建议修改 composer.json
文件,然后 composer update
安装。
1 2 3 "require" : { "laravel/passport" : "~4.0" }
配置 执行迁移 框架会自动生成 passport 所需要的数据表
生成加密密钥 1 $ php artisan passport:install
使用 HasApiTokens 在认证用的 model 中添加 HasApiTokens
Trait,并且 model 要继承 Illuminate\Foundation\Auth\User
1 2 3 4 5 6 7 8 9 10 11 <?php namespace App \Models ;use Illuminate \Foundation \Auth \User as Authenticatable ;use Laravel \Passport \HasApiTokens ;class AdminUser extends Authenticatable { use HasApiTokens ; }
更改 guard 和 provider 在 config/auth.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 'guards' => [ 'web' => [ 'driver' => 'session' , 'provider' => 'users' , ], 'admin' => [ 'driver' => 'passport' , 'provider' => 'admin_users' , ], ], 'providers' => [ 'users' => [ 'driver' => 'eloquent' , 'model' => App\Models\User::class, ], 'admin_users' => [ 'driver' => 'eloquent' , 'model' => App\Models\AdminUser::class, ], ],
使用 到这一步,可以开始实现登录的逻辑了,我希望验证的是 admin_users
表里的管理用户,但是根据其他教程的配置,发现一直验证的都是 users
表。最后看了源码,发现这么一个大坑。src/Bridge/UserRepository.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public function getUserEntityByUserCredentials ($username, $password, $grantType, ClientEntityInterface $clientEntity) { $provider = config('auth.guards.api.provider' ); if (is_null($model = config('auth.providers.' .$provider.'.model' ))) { throw new RuntimeException('Unable to determine authentication model from configuration.' ); } if (method_exists($model, 'findForPassport' )) { $user = (new $model)->findForPassport($username); } else { $user = (new $model)->where('email' , $username)->first(); } · · · }
这段源码可以发现两个问题。第一个,passport 认证的 model 是 auth.guards.api.provider
里定义的 model。 第二个,认证默认验证的字段是 email
,如果你的用户名是其他的字段,那么就要自己在认证的model里实现 findForPassport
方法。发现了坑,就想办法解决掉,下面是具体的使用方法,可以供大家参考。
创建一个登录认证的 trait 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 <?php namespace App \Http \Controllers \Admin \Traits ;use GuzzleHttp \Client ;use GuzzleHttp \Exception \RequestException ;trait ProxyHelpers{ public function authenticate ($guard = '' ) { $client = new Client(); try { $url = request()->root() . '/admin/oauth/token' ; $params = array_merge(config('passport.proxy' ), [ 'username' => request('username' ), 'password' => request('password' ), 'provider' => $guard ]); $respond = $client->post($url, ['form_params' => $params]); } catch (RequestException $exception) { abort(401 , $exception->getMessage()); } if ($respond->getStatusCode() !== 401 ) { return json_decode($respond->getBody()->getContents(), true ); } abort(401 , '账号或密码错误' ); } public function getRefreshToken () { $client = new Client(); try { $url = request()->root() . 'admin/oauth/token' ; $params = array_merge(config('passport.proxy' ), [ 'refresh_token' => request('refresh_token' ), ]); $respond = $client->post($url, ['form_params' => $params]); } catch (RequestException $exception) { abort(401 , '请求失败,服务器错误' ); } if ($respond->getStatusCode() !== 401 ) { return json_decode($respond->getBody()->getContents(), true ); } abort(401 , 'refresh_token 错误' ); } }
登录控制器 使用上面定义的 ProxyHelpers trait 来完成认证,响应部分可以参考 Laravel5.5+passport 放弃 dingo 开发 API 实战,让 API 开发更省心 的内容来实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 <?php namespace App \Http \Controllers \Admin ;use App \Http \Controllers \Admin \Traits \ProxyHelpers ;use App \Http \Requests \Admin \LoginRequest ;use App \Models \AdminUser ;use Carbon \Carbon ;use Illuminate \Foundation \Auth \AuthenticatesUsers ;use Illuminate \Http \Request ;use Illuminate \Support \Facades \Auth ;use Illuminate \Support \Facades \Hash ;class LoginController extends Controller { use AuthenticatesUsers , ProxyHelpers ; public function login (LoginRequest $request) { $user = AdminUser::query()->where('username' , $request->username)->first(); if (!$user) { return $this ->failed('用户不存在' , 401 ); } if (!Hash::check($request->password, $user->password)) { return $this ->failed('密码不正确' ); } $user->last_login_at = Carbon::now()->toDateTimeString(); $user->save(); $token = $this ->authenticate(‘admin’); return $this ->success(['token' => $token, 'user' => $user]); } public function logout () { if (Auth::guard('admin' )->check()) { Auth::guard('admin' )->user()->token()->delete(); } } }
创建一个处理请求的中间件 PassportCustomProvider
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?php namespace App \Http \Middleware ;use Closure ;use Illuminate \Support \Facades \Config ;class PassportCustomProvider { public function handle ($request, Closure $next) { $params = $request->all(); if (array_key_exists('provider' , $params)) { Config::set('auth.guards.api.provider' , $params['provider' ]); } return $next($request); } }
注册路由中间件 在 app\Http\Kernel.php
中注册配置的路由
1 2 3 4 5 6 7 8 9 protected $routeMiddleware = [ 'auth' => \Illuminate\Auth\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'passport-administrators' => \App\Http\Middleware\PassportCustomProvider::class ];
配置 passport 路由 在 app/Providers/AuthServiceProvider.php
1 2 3 4 5 6 7 8 9 10 11 12 class AuthServiceProvider extends ServiceProvider { public function boot () { $this ->registerPolicies(); Passport::routes(function (RouteRegistrar $router) { $router->forAccessTokens(); }, ['prefix' => 'admin/oauth' , 'middleware' => 'passport-administrators' ]); } }
最后配置 LoginController 的路由,然后就发起请求吧!
参考资料
passport API 认证 – 多表登录
Laravel Passport 踩坑日记
Laravel 5.5 使用 Passport 实现 Auth 认证
Laravel5.5+passport 放弃 dingo 开发 API 实战,让 API 开发更省心