1. 程式人生 > >lumen驗證解析和使用jwt做介面驗證

lumen驗證解析和使用jwt做介面驗證

好久沒寫 PHP 程式碼了,尤其是 Lumen,我是 Lumen 的忠實使用者,自從面世開始,我就將 Lumen 作為我 API 的主要框架使用。

但說到 API,不得不說的一個概念:「前後端分離」,現在越來越多的團隊都採用前後端分離,徹底解放出前端的優勢,也讓後臺更加集中於資料的輸出。關於這方面的討論,不在這裡討論了,可以參考一些文章深入研究:

正因為有了前後端分離,後臺關注於介面 API 的輸出,當時 Lumen 的出現,就是為 RESTful API 而生的:

6302-2c2d3495ea94d0a5.jpg

Decidedly Laravel. Delightfully Minimal.

Lightning fast micro-services and APIs delivered with the elegance you expect.

將 Lumen 作為介面框架使用,不得不解決一個核心問題:如何對訪問者進行「認證」。

使用者認證

Lumen 雖然與 Laravel 使用了相同的底層類庫實現,但是因 Lumen 面向的是無狀態 API 的開發,不支援 session,所以預設的配置不同。Lumen 必須使用無狀態的機制來實現,如 API 令牌(Token)。

我們看看 Lumen 官網提供的例子:

use Illuminate\Http\Request;

$app->get('/post/{id}', ['middleware' => 'auth', function (Request $request, $id) {

$user = Auth::user();

$user = $request->user();

//

}]);

其中使用了中介軟體:'middleware' => 'auth',我們看看 auth 中介軟體函式:

$app->routeMiddleware([

'auth' => App\Http\Middleware\Authenticate::class,

]);

關聯的是 Authenticate 類,我們看 Authenticate 的 handle 函式:

/**

* Handle an incoming request.

*

* @param \Illuminate\Http\Request $request

* @param \Closure $next

* @param string|null $guard

* @return mixed

*/

public function handle($request, Closure $next, $guard = null)

{

if ($this->auth->guard($guard)->guest()) {

return response('Unauthorized.', 401);

}

return $next($request);

}

首先會判斷$this->auth->guard($guard)->guest()。我們繼續跟進程式碼到 AuthManager 中:

/**

* Attempt to get the guard from the local cache.

*

* @param string $name

* @return \Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard

*/

public function guard($name = null)

{

$name = $name ?: $this->getDefaultDriver();

return isset($this->guards[$name])

? $this->guards[$name]

: $this->guards[$name] = $this->resolve($name);

}

預設傳入的 $name = null,所以我們看看 $this->getDefaultDriver()

/**

* Get the default authentication driver name.

*

* @return string

*/

public function getDefaultDriver()

{

return $this->app['config']['auth.defaults.guard'];

}

這就到了預設的配置 config 中了:

6302-e82f4fc242988f62.jpg

從 Lumen 原始碼中可以看出 Lumen 的預設認證方式「api」。

我們再來看看 Laravel 的預設認證方式:

6302-0cbb0bd3263207dd.jpg

Laravel 預設採用「web」方式,而 web 方式是使用 session 來進行使用者認證。這也就很好的說明了 Lumen 的無狀態性。

接著我們需要明白 Lumen 如何通過「api」來進行使用者認證的。

AuthServiceProvider 存放在 app/Providers 資料夾中,此檔案中只有一個 Auth::viaRequest 呼叫。viaRequest 會在系統需要認證的時候被呼叫,此方法接受一個匿名函式傳參,在此匿名函式內,你可以任意的解析 App\User 並返回,或者在解析失敗時返回 null,如:

/**

* Boot the authentication services for the application.

*

* @return void

*/

public function boot()

{

// Here you may define how you wish users to be authenticated for your Lumen

// application. The callback which receives the incoming request instance

// should return either a User instance or null. You're free to obtain

// the User instance via an API token or any other method necessary.

$this->app['auth']->viaRequest('api', function ($request) {

if ($request->input('api_token')) {

return User::where('api_token', $request->input('api_token'))->first();

}

});

}

我們來看看 viaRequest 函式:

/**

* Register a new callback based request guard.

*

* @param string $driver

* @param callable $callback

* @return $this

*/

public function viaRequest($driver, callable $callback)

{

return $this->extend($driver, function () use ($callback) {

$guard = new RequestGuard($callback, $this->app['request'], $this->createUserProvider());

$this->app->refresh('request', $guard, 'setRequest');

return $guard;

});

}

這裡關鍵的是 RequestGuard,這個類的核心函式:

/**

* Get the currently authenticated user.

*

* @return \Illuminate\Contracts\Auth\Authenticatable|null

*/

public function user()

{

// 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;

}

return $this->user = call_user_func(

$this->callback, $this->request, $this->getProvider()

);

}

這個是判斷是否獲取使用者資訊,主要是呼叫callback 函式,而這個函式就是我們從 viaRequest 傳入的:

function ($request) {

if ($request->input('api_token')) {

return User::where('api_token', $request->input('api_token'))->first();

}

}

而這只是舉一個驗證使用者的例子,判斷請求是否傳入 api_token引數,並通過 User Model 直接匹配查詢獲取 User or null。

當然在實際開發中,我們不能只是簡單的獲取 api_token直接關聯資料庫查詢使用者資訊。

在 API 開發中,使用者認證是核心,是資料是否有保障的前提,目前主要有兩種常用方式進行使用者認證: JWT 和 OAuth2。

下一步

當前只是對 Lumen 的「使用者認證」進行簡單的瞭解,下一步通過對 「JWT」的學習,來看看如何利用 JWT 來有效的使用者認證,更加安全的保障介面資訊被有效的使用者訪問。

附:
Json web token (JWT), 是為了在網路應用環境間傳遞宣告而執行的一種基於JSON 的開放標準 (RFC 7519)。該 token 被設計為緊湊且安全的,特別適用於分散式站點的單點登入(SSO)場景。JWT 的宣告一般被用來在身份提供者和服務提供者間傳遞被認證的使用者身份資訊,以便於從資源伺服器獲取資源,也可以增加一些額外的其它業務邏輯所必須的宣告資訊,該 token 也可直接被用於認證,也可被加密。

關於 JWT 更具體的介紹,相信網上有很多帖子和文章值得參考,這裡先不闡述了。

為了學習 JWT 在 Lumen 中的使用,最好的辦法就是在「程式設計師同志網 —— GitHub」搜尋有關外掛,找個 stars 最多的那個拿來研究研究。

6302-8f69587a95647335.jpg

image

tymondesigns/jwt-auth

JSON Web Token Authentication for Laravel & Lumen

6302-984fc20a149948e8.png

image

安裝 jwt-auth

通過 Composer 安裝:

composer require tymon/jwt-auth:"^[email protected]"

6302-320b487f44890a8a.jpg

image

注: 0.5.* 版本未對 Lumen 專門做封裝

將 $app->withFacades() 和 auth 認證相關的註釋去掉:

<?php

require_once __DIR__.'/../vendor/autoload.php';

try {

(new Dotenv\Dotenv(__DIR__.'/../'))->load();

} catch (Dotenv\Exception\InvalidPathException $e) {

//

}

/*

|--------------------------------------------------------------------------

| Create The Application

|--------------------------------------------------------------------------

|

| Here we will load the environment and create the application instance

| that serves as the central piece of this framework. We'll use this

| application as an "IoC" container and router for this framework.

|

*/

$app = new Laravel\Lumen\Application(

realpath(__DIR__.'/../')

);

// 取消註釋,這樣就可以通過 Auth::user(),獲取當前授權使用者

$app->withFacades();

$app->withEloquent();

/*

|--------------------------------------------------------------------------

| Register Container Bindings

|--------------------------------------------------------------------------

|

| Now we will register a few bindings in the service container. We will

| register the exception handler and the console kernel. You may add

| your own bindings here if you like or you can make another file.

|

*/

$app->singleton(

Illuminate\Contracts\Debug\ExceptionHandler::class,

App\Exceptions\Handler::class

);

$app->singleton(

Illuminate\Contracts\Console\Kernel::class,

App\Console\Kernel::class

);

/*

|--------------------------------------------------------------------------

| Register Middleware

|--------------------------------------------------------------------------

|

| Next, we will register the middleware with the application. These can

| be global middleware that run before and after each request into a

| route or middleware that'll be assigned to some specific routes.

|

*/

// $app->middleware([

// App\Http\Middleware\ExampleMiddleware::class

// ]);

// 增加 auth 中介軟體

$app->routeMiddleware([

'auth' => App\Http\Middleware\Authenticate::class,

]);

/*

|--------------------------------------------------------------------------

| Register Service Providers

|--------------------------------------------------------------------------

|

| Here we will register all of the application's service providers which

| are used to bind services into the container. Service providers are

| totally optional, so you are not required to uncomment this line.

|

*/

$app->register(App\Providers\AppServiceProvider::class);

$app->register(App\Providers\AuthServiceProvider::class);

// $app->register(App\Providers\EventServiceProvider::class);

/*

|--------------------------------------------------------------------------

| Load The Application Routes

|--------------------------------------------------------------------------

|

| Next we will include the routes file so that they can all be added to

| the application. This will provide all of the URLs the application

| can respond to, as well as the controllers that may handle them.

|

*/

$app->router->group([

'namespace' => 'App\Http\Controllers',

], function ($router) {

require __DIR__.'/../routes/web.php';

});

return $app;

然後在 AppServiceProvider 中註冊 LumenServiceProvider:

$this->app->register(\Tymon\JWTAuth\Providers\LumenServiceProvider::class);

在 Lumen 專案中,預設沒有 config 資料夾,需要在專案根目錄建立,並將 vendor 原始碼中auth.php 複製出來,同時將 api 認證指定為「jwt」:

<?php

return [

/*

|--------------------------------------------------------------------------

| Authentication Defaults

|--------------------------------------------------------------------------

|

| This option controls the default authentication "guard" and password

| reset options for your application. You may change these defaults

| as required, but they're a perfect start for most applications.

|

*/

'defaults' => [

'guard' => env('AUTH_GUARD', 'api'),

],

/*

|--------------------------------------------------------------------------

| Authentication Guards

|--------------------------------------------------------------------------

|

| Next, you may define every authentication guard for your application.

| Of course, a great default configuration has been defined for you

| here which uses session storage and the Eloquent user provider.

|

| All authentication drivers have a user provider. This defines how the

| users are actually retrieved out of your database or other storage

| mechanisms used by this application to persist your user's data.

|

| Supported: "session", "token"

|

*/

'guards' => [

'api' => [

'driver' => 'jwt',

'provider' => 'users'

],

],

/*

|--------------------------------------------------------------------------

| User Providers

|--------------------------------------------------------------------------

|

| All authentication drivers have a user provider. This defines how the

| users are actually retrieved out of your database or other storage

| mechanisms used by this application to persist your user's data.

|

| If you have multiple user tables or models you may configure multiple

| sources which represent each model / table. These sources may then

| be assigned to any extra authentication guards you have defined.

|

| Supported: "database", "eloquent"

|

*/

'providers' => [

'users' => [

'driver' => 'eloquent',

'model' => \App\User::class,

],

],

/*

|--------------------------------------------------------------------------

| Resetting Passwords

|--------------------------------------------------------------------------

|

| Here you may set the options for resetting passwords including the view

| that is your password reset e-mail. You may also set the name of the

| table that maintains all of the reset tokens for your application.

|

| You may specify multiple password reset configurations if you have more

| than one user table or model in the application and you want to have

| separate password reset settings based on the specific user types.

|

| The expire time is the number of minutes that the reset token should be

| considered valid. This security feature keeps tokens short-lived so

| they have less time to be guessed. You may change this as needed.

|

*/

'passwords' => [

//

],

];

最後,因為 JWT 協議需要用到 secret,所以需要生成一個 secret:

php artisan jwt:secret

6302-e14f9aa6bb083d08.jpg

image

使用 jwt-auth

1. 更新 User Model

繼承 Tymon\JWTAuth\Contracts\JWTSubject:

<?php

namespace App;

use Illuminate\Auth\Authenticatable;

use Laravel\Lumen\Auth\Authorizable;

use Illuminate\Database\Eloquent\Model;

use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;

use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;

use Tymon\JWTAuth\Contracts\JWTSubject;

class User extends Model implements AuthenticatableContract, AuthorizableContract, JWTSubject

{

use Authenticatable, Authorizable;

/**

* The attributes that are mass assignable.

*

* @var array

*/

protected $fillable = [

'name', 'email',

];

/**

* The attributes excluded from the model's JSON form.

*

* @var array

*/

protected $hidden = [

'password',

];

/**

* Get the identifier that will be stored in the subject claim of the JWT.

*

* @return mixed

*/

public function getJWTIdentifier()

{

return $this->getKey();

}

/**

* Return a key value array, containing any custom claims to be added to the JWT.

*

* @return array

*/

public function getJWTCustomClaims()

{

return [];

}

}

2. 寫一個 Login 方法,驗證登陸資訊,並返回 token 回客戶端:

// 路由

$router->post('/auth/login', '[email protected]');

postLogin 方法:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use Tymon\JWTAuth\JWTAuth;

class AuthController extends Controller

{

protected $jwt;

public function __construct(JWTAuth $jwt)

{

$this->jwt = $jwt;

}

public function postLogin(Request $request)

{

if (! $token = $this->jwt->attempt($request->only('email', 'password'))) {

return response()->json(['user_not_found'], 404);

}

return response()->json(compact('token'));

}

}

可以請求試試了,用 Postman 跑跑:

6302-245e2a148a9ea91b.jpg

image

有了 token 了。我們就可以用來測試,看能不能認證成功,獲取使用者資訊。

3. 使用 token 獲取使用者資訊

// 使用 auth:api 中介軟體

$router->group(['middleware' => 'auth:api'], function($router)

{

$router->get('/test', '[email protected]');

});

只要驗證通過,就可以利用 Auth:user()方法獲取使用者資訊了。

public function getUser(Request $request) {

return response()->json(['user' => Auth::user()]);

}

6302-dbc6a7288c90740d.jpg

image

對照資料庫:

6302-6ef3747ff32fe3a7.jpg

image

以後只要在請求的 headers 中加入 token 資訊即可,完美實現使用者認證。

總結

對獲取到 token 值 (eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vZGVtby5hcHAvYXV0aC9sb2dpbiIsImlhdCI6MTUxMDQ3NTQ5MiwiZXhwIjoxNTEwNDc5MDkyLCJuYmYiOjE1MTA0NzU0OTIsImp0aSI6Imx3UFpSMTN0MlV5eXRib1oiLCJzdWIiOjEsInBydiI6Ijg3ZTBhZjFlZjlmZDE1ODEyZmRlYzk3MTUzYTE0ZTBiMDQ3NTQ2YWEifQ.YTvsiO9MT3VgPZiI03v2sVEIsGLj8AUwJiDuXvCAvHI) 仔細觀察,就會發現中間是由兩個「.」來合併三段資訊的。

下一步我們就來研究研究 JWT 的原理和也可以自己動手寫個基於 JWT 的 Lumen 認證外掛出來。

相關推薦

lumen驗證解析使用jwt介面驗證

好久沒寫 PHP 程式碼了,尤其是 Lumen,我是 Lumen 的忠實使用者,自從面世開始,我就將 Lumen 作為我 API 的主要框架使用。 但說到 API,不得不說的一個概念:「前後端分離」,現在越來越多的團隊都採用前後端分離,徹底解放出前端的優勢,也讓後臺更加集中

傳統身份驗證JWT的身份驗證

轉自:https://ninghao.net/blog/2834 1、傳統身份驗證和JWT的身份驗證 傳統身份驗證: HTTP 是一種沒有狀態的協議,也就是它並不知道是誰是訪問應用。這裡我們把使用者看成是客戶端,客戶端使用使用者名稱還有密

javascript預解析函式引數

1. 預解析:提前解析程式碼 console.log("num");// 輸出-undefined var num=10; 預解析是: var num; console.log("num");// 輸出-undefined num=10; 2. arguments 物件 可將獲取的多個值

超引數、驗證K-折交叉驗證

- 本文首發自公眾號:[RAIS](https://ai.renyuzhuo.cn/img/wechat_ercode.png) ## ​前言 本系列文章為 《Deep Learning》 讀書筆記,可以參看原書一起閱讀,效果更佳。 ## 超引數 - 引數:網路模型在訓練過程中不斷學習自動調節的變數,

Django Restful Framework【第四篇】版本、解析器、序列化請求數據驗證

tor ade clu cts scheme com 繼承 src 當前 一、版本 程序也來越大時,可能通過版本不同做不同的處理 沒用rest_framework之前,我們可以通過以下這樣的方式去獲取。 class UserView(APIView): def

基於gin框架jwt-go中間件實現小程序用戶登陸token驗證

expires 微信 處理 如果 .net local 切入點 小程序 err 本文核心內容是利用jwt-go中間件來開發golang webapi用戶登陸模塊的token下發和驗證,小程序登陸功能只是一個切入點,這套邏輯同樣適用於其他客戶端的登陸處理。 小程序登陸邏輯

SpringCloud利用閘道器攔截Token驗證JWT方式) SpringCloud利用閘道器攔截Token驗證JWT方式)

SpringCloud利用閘道器攔截做Token驗證(JWT方式) 2018年09月29日 15:51:50 19八9 閱讀數:23 更多

有登入驗證的情況下,介面請求

qycloud這種方式是可以的 import requests import pprint def login_api(): values = { "username": "apitest", "password": "111111",

使用shiro安全驗證授權管理

apache shiro    不依賴於spring spring security 目標:  ssm+shiro shiro的主要功能 1.  使用者認證  (登入)       Authenticator 2.  授權 (使用者的訪問的控制) shiro的架構組成

javascript中的提交後臺手機驗證後臺郵箱的介面

function gettokenMobile(mydata,mytype) { //封裝newdata var newdata = { 'mobile': mytype, '

asp.net core 3.1多種身份驗證方案,cookiejwt混合認證授權

開發了一個公司內部系統,使用asp.net core 3.1。在開發使用者認證授權使用的是簡單的cookie認證方式,然後開發好了要寫幾個介面給其它系統呼叫資料。並且只是幾個簡單的介面不準備再重新部署一個站點,所以就直接在MVC的專案裡面加了一個API區域用來寫介面。這時候因為是介面所以就不能用cookie方

繞過010Editor網絡驗證(用python一個仿真http server真容易,就幾行代碼)

headers redirect 如果 table 本地 align cnn 破解版 resp 010Editor是一款非常強大的十六進制編輯器,尤其是它的模板功能在分析文件格式時相當好用!網上現在也有不少010Editor的破解版,如果沒錢或者舍不得花錢買授權的話,去官方

ajax的學習運用ajax來驗證用戶名是否存在的例子

學習 進行 過程 一個 success 並且 驗證用戶名 size 用戶 ajax執行的過程:  首先是由JS頁面發送一個請求,並且傳送一個data數據過去, 交給處理頁面來對數據庫進行處理,處理完了就返回一個值當作success 方法裏面的參數來執行success裏面的回

物聯網MQTT協議分析開源Mosquitto部署驗證

-h etc 遙感 並且 傳輸 物聯網平臺 發布消息 情況 all 在《物聯網核心協議—消息推送技術演進》一文中已向讀者介紹了多種消息推送技術的情況,包括HTTP單向通信、Ajax輪詢、Websocket、MQTT、CoAP等,其中MQTT協議為IBM制定並力推

nginx安裝測試 (已驗證

fig figure lib pan 首頁 min 正常 如果 nginx安裝 進入:/usr/local/nginx 目錄註意:為了保證各插件之間的版本兼容和穩定,建議先通過以下版本進行測試驗證。一、下載版本 下載nginx: wget http://nginx.o

驗證猜數字

etl clas and 定義 visible 登錄 功能 number image 實驗任務四 1,出現設計思想 (1)先定義文本框、密碼框和驗證碼框的組件 (2)定義面板和按鈕的個數 (3)定義公有的虛構方法,通過對象實例化來調用 (4)利用Random類來實現生成0-

js表單驗證處理childNodes children 的區別

.get out ren 改名 表單 tex jquery this 如果 一、對提交表單進行空值驗證 html代碼: 1   <form action="#"onsubmit="return validate_form(this);" method="post"&

發送短信驗證郵箱驗證碼—Java實現

短信驗證碼 郵箱驗證碼 短信驗證碼 短信驗證碼都是調用一些接口來進行短信的發送,短信驗證碼在登錄、註冊等操作中使用的最廣泛,本文這一節演示如何使用Java制作一個簡單的短信驗證碼登錄。 我這裏演示使用的是聚合數據的短信接口(並非廣告),因為聚合數據的接口調用比較方便和簡單,所以首先得先去聚合數據裏

代碼驗證ncut譜聚類的系數

mark div dia source nes pos cnblogs 驗證 rand W = rand(30); W = W+W'; I = cell(3,1); I{1} = 1:10; I{2} = 11:20; I{3} = 21:30; vol

php表單 - 驗證郵件URL

通過 image all e-mail translate view 進行 pattern log PHP - 驗證名稱 以下代碼將通過簡單的方式來檢測 name 字段是否包含字母和空格,如果 name 字段值不合法,將輸出錯誤信息: $name =