利用Laravel 搭建oauth2 API接口
要求
laravel 5.4以上
安裝
$ composer require laravel/passport
在配置文件 config/app.php 的providers 數組中注冊 Passport 服務提供者:
LaravelPassportPassportServiceProvider::class,
遷移數據庫 執行完后會生成oauth需要的表
$ php artisan migrate
這一步注意,執行的時候可能會報錯
Syntax error or access violation: 1071 Specified key was too long; max key length is 767 byte
這是由於 Laravel5.4默認使用utf8mb4 編碼
utf8 最大長度字符是3字節 utf8mb4是4字節
解決方法就是
-
數據庫改用utf8mb4
-
AppServiceProvider.php里面加上Schema::defaultStringLength(191);
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Facades\Schema; class AppServiceProvider extends ServiceProvider { /** * Bootstrap any application services. * * @return void */ public function boot() { // Schema::defaultStringLength(191); } /** * Register any application services. * * @return void */ public function register() { // } }
另外Mysql 5.5.3之后才支持utf8mb4也需要注意下
接下來執行
$ php artisan passport:install
會生成兩個客戶端密鑰
Client ID: 1
Client Secret: AwDMcCs65rXkzF80wPaINx5fkoXEfa8lcuuPEvQK
Password grant client created successfully. Client ID: 2 Client Secret: KLlLijWk3hX2Ntfzo2iFPgwT4GyITpBjEuDozp5H
配置
這里可以配置的只有access token的生命周期默認是永久的
在AuthServiceProvider中配置
use Carbon\Carbon; use Laravel\Passport\Passport; /** * 注冊所有認證/授權服務. * * @return void */ public function boot() { $this->registerPolicies(); Passport::routes(); Passport::tokensExpireIn(Carbon::now()->addDays(15)); Passport::refreshTokensExpireIn(Carbon::now()->addDays(30)); }
修改auth.php
'guards'['driver'] => 'passport'
發放access_token
頒發
應用場景 我的用戶,在別的網站想用我的賬號直接登錄,參考微信登錄。那么第三方網站就要對接過來,用戶選擇第三方登錄,跳轉到我的頁面,詢問用戶是否允許,用戶允許以后我會帶一個code回去,第三方網站用這個code請求access_token
流程是
請求令牌
'client_id' => 'client-id', 'redirect_uri' => 'http://example.com/callback', 'response_type' => 'code', 'scope' => '',
用戶允許以后拿到code換token
$response = $http->post('http://your-app.com/oauth/token', [ 'form_params' => [ 'grant_type' => 'authorization_code', 'client_id' => 'client-id', 'client_secret' => 'client-secret', 'redirect_uri' => 'http://example.com/callback', 'code' => $request->code, ], ]);
token如果過期了,可以刷新
$response = $http->post('http://your-app.com/oauth/token', [ 'form_params' => [ 'grant_type' => 'refresh_token', 'refresh_token' => 'the-refresh-token', 'client_id' => 'client-id', 'client_secret' => 'client-secret', 'scope' => '', ], ]);
賬號密碼
這個主要是用於APP(我自己的),用戶通過app輸入賬號和密碼,我用賬號密碼校驗正確了就發送access_token
$response = $http->post('http://your-app.com/oauth/token', [ 'form_params' => [ 'grant_type' => 'password', 'client_id' => 'client-id', 'client_secret' => 'client-secret', 'username' => 'taylor@laravel.com', 'password' => 'my-password', 'scope' => '', ], ]);
隱式
這種跟第一種差不多,就是省去了code 直接發放,主要用於
JavaScript 或移動應用中客戶端登錄認證信息不能保存時
客戶端證書
這種主要用於機器之間的通信
直接用appid 和 appsecret 換令牌
$response = $guzzle->post('http://your-app.com/oauth/token', [ 'form_params' => [ 'grant_type' => 'client_credentials', 'client_id' => 'client-id', 'client_secret' => 'client-secret', 'scope' => 'your-scope', ], ]);
私人訪問令牌
這個用於在程序里面調用API的時候
比如
$user = App\User::find(1); // 創建一個不帶域的令牌... $token = $user->createToken('Token Name')->accessToken; // 創建一個帶域的令牌... $token = $user->createToken('My Token', ['place-orders'])->accessToken;
在調用api之前需要創建client
創建命令是$ php artisan passport:client
密碼和私人的不同其他都一樣
$ php artisan passport:client --password
$ php artisan passport:client --personal
創建好后獲得client-id和client-secret
創建路由
5.4以后目錄結構發生變化,路由統一寫在routes文件夾下。
API的路由都寫在api.php
確定好路由就可以請求接口了
GET 方式
/api/user
'headers' => [ 'Accept' => 'application/json', 'Authorization' => 'Bearer '.$accessToken, ],
寫到這里遇到一個問題
就是無論怎樣請求 獲取到的token 用來訪問接口的時候 總是返回
Unauthenticated
GOOGLE了下發現好多人也遇到這個問題,據說是token過期時間的問題
在AuthServiceProvider boot里面加上
Passport::tokensExpireIn(Carbon::now()->addDays(15)); Passport::refreshTokensExpireIn(Carbon::now()->addDays(30));
這樣應該會解決,然而並沒有,這里等以后一時間再研究下(已解決 見下文)
這個問題有了一定進展
目前通過用戶授權頒發令牌的方式通過了
前提是用戶必須登錄,之前返回Unauthenticated 應該是因為用戶未登錄
在應用站跳轉到授權站的時候,此時用戶需登錄狀態,授權以后拿到code再來換access_token 這個方式OK的,可以正常獲取登錄用戶的信息
賬號密碼獲取令牌的方式也一樣可以通過
站點之前通過 id 和 secret的方式換token,然后拿token請求接口這種方式目前還不行
坑爹啊,官方文檔沒寫全
通過 client_credentials 方式獲取token,請求接口的時候,路由不能用auth或者scope等中間件去驗證,因為他們會首先驗證有沒有登錄。
我們需要在app\Http\Kernel.php 的 $routeMiddleware 里面定義一個客戶端API的中間件
'client_credentials' => \Laravel\Passport\Http\Middleware\CheckClientCredentials::class,
然后在路由里面Route::middleware('client_credentials')
或者
Route::middleware('client_credentials:作用域名稱')
這樣就可以實現不登錄直接調用api了
參考文檔