Laravel 5.3 中使用 Laravel Passport 构建后端授权认证 API
Laravel在5.3中引入了新的官方OAuth扩展Laravel Passport,之前在5.1/5.2时一直是用dingo+jwt这一套来构建后端api,最近正好要构建新项目,想着试试官方这一个拓展如何。
安装
官方文档中有完整的安装调用过程,使用composer:
composer require laravel/passport
像使用其他组件一样,我们需要在config/app/php
的providers
数组中注册Passport:
Laravel\Passport\PassportServiceProvider::class,
然后我们需要执行migrate
创建相关的客户端数据表和令牌数据表,和passport:install
来生成一些加密秘钥等,这些在官方文档中都有详细介绍。
构建认证函数
官方文档中针对如何简单使用做了初步介绍,下面我试着构造一个完整的客户端授权流程,首先创建ApiController
:
php artisen make:controller Apicontroller
在ApiController
中引入AuthenticatesUsers
模块:
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
class ApiController extends Controller
{
use AuthenticatesUsers;
public function __construct()
{
$this->middleware('api');
}
我们的客户端需要通过密码授权的方式来认证,我们需要在ApiController
中重写AuthenticatesUsers
部分功能函数来实现整个完整的授权流程,在这里我们调用Passport提供的oauth/token
接口:
//调用认证接口获取授权码
protected function authenticateClient(Request $request)
{
$credentials = $this->credentials($request);
$data = $request->all();
$request->request->add([
'grant_type' => $data['grant_type'],
'client_id' => $data['client_id'],
'client_secret' => $data['client_secret'],
'username' => $credentials['phone'],
'password' => $credentials['password'],
'scope' => ''
]);
$proxy = Request::create(
'oauth/token',
'POST'
);
$response = \Route::dispatch($proxy);
return $response;
}
//以下为重写部分
protected function authenticated(Request $request)
{
return $this->authenticateClient($request);
}
protected function sendLoginResponse(Request $request)
{
$this->clearLoginAttempts($request);
return $this->authenticated($request);
}
protected function sendFailedLoginResponse(Request $request)
{
$msg = $request['errors'];
$code = $request['code'];
return $this->failed($msg,$code);
}
在这里我们会遇到一个问题,官方文档中没有提及,就是我们要如何使用自定的用户名进行授权,找寻源码,其实在Laravel\Passport\Bridge\UserRepository.php
的getUserEntityByUserCredentials()
函数中会看到这段代码:
if (method_exists($model, 'findForPassport')) {
$user = (new $model)->findForPassport($username);
} else {
$user = (new $model)->where('email', $username)->first();
}
我们只需要在我们配置在config/auth.php的模型中添加这段代码就可以完成自定义授权用户名,该方法和社区中另一个帖子方法一样:
public function findForPassport($username) {
return $this->where('phone', $username)->first();
}
这样一个授权流程基本完成,接下来创建一个Api/LoginController
:
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\ApiController;
use Illuminate\Http\Request;
use App\Models\User; \\User.php我移动到了Models目录
use Validator;
class LoginController extends ApiController
{
// 登录用户名标示为phone字段
public function username()
{
return 'phone';
}
//登录接口,调用了ApiController中一些其他函数succeed\failed,上文未提及,用于接口格式化输出
public function login(Request $request)
{
$validator = Validator::make($request->all(), [
'phone' => 'required|exists:users',
'password' => 'required|between:6,32',
]);
if ($validator->fails()) {
$request->request->add([
'errors' => $validator->errors()->toArray(),
'code' => 401,
]);
return $this->sendFailedLoginResponse($request);
}
$credentials = $this->credentials($request);
if ($this->guard('api')->attempt($credentials, $request->has('remember'))) {
return $this->sendLoginResponse($request);
}
return $this->failed('login failed',401);
}
}
构建路由
最后routes/api.php
中加入我们需要的路由:
Route::group([
'prefix'=>'/v1',
'middleware' => ['api']
], function () {
Route::post('/user/login','Api\LoginController@login');
});
测试
最后在postmen中调用接口:
结果正确返回。
比jwt好用吗???
為什麼不直接使用 passport::route() ?
例如這樣, 只開放 /oauth/token
@jl9404 大神请教一下,前后端分离项目,前端每次登录请求/oauth/token都会创建一个新的access_token,怎样做判断,如果用户已有未过期的access_token 就直接返回该access_token 如果没有,再创建?
@jl9404 想问一下只开发 /oauth/token来生成access token的时候,怎么实现多种方式的登录呢
@Jinrenjie
Laravel passport 都是用 JWT 但這 cookie 好像會跟 session.lifetime 的設定
可能你需要加多一個中間件去擭取 JWT 內的 refresh token 拿新的 access token
你可以試試看
https://learnku.com/docs/laravel/5.3/passp...
@zhaohehe
只用
Passport::routes();
就開放全部方式了@jl9404 你的意思是前端直接访问/oauth/token来拿到access token吗
@jl9404 这个职能在本地项目里用吧,我想实现前后端完全分离 不同域名
config/auth.php这个文件可以添加public方法???
我一开始用passport的password模式来做登录授权,但后来加入第三方登录后,不知道怎么手动生成access_token和refresh_token,有哪位大神碰到相同的问题?急,在线等
@jl9404 前端要隐藏client_id、client_secret 所以用自己的接口
客户端认证的,应该怎么配置,我password认证方式,正常,客户端认证的就返回500
不通过账号密码登陆的该怎么整
难道只有我一个人看不懂吗?这个不仍旧是用的SessionGard吗?好像和Passport没有关系啊,我看Taylor Otwell录的视频,他讲的意思好像是我们做后台接口,然后可以给很多人来调用到她们自己的网站,这里面每个网站来取数据的时候就需要用到api_token验证,我英语不太好,希望有人和我交流下,我可以把Laracast上购买的视频相赠。多谢了。
@zhaohehe 通过博主的findForPassport方法改编