安裝 tymon/jwt-auth 包
compoer require tymon/jwt-auth
laravel 版本 <= 5.4,需要在 config/app.php 檔案中的 providers 陣列中新增
'providers' => [
...
/*
* Package Service Providers...
*/
Tymon\JWTAuth\Providers\LaravelServiceProvider::class, // jwt
...
]
生成祕鑰
php artisan jwt:secret
釋出配置
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
調整配置
<?php
/*
* This file is part of jwt-auth.
*
* (c) Sean Tymon <tymon148@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
return [
/*
|--------------------------------------------------------------------------
| JWT Authentication Secret
|--------------------------------------------------------------------------
|
| Don't forget to set this in your .env file, as it will be used to sign
| your tokens. A helper command is provided for this:
| `php artisan jwt:secret`
|
| Note: This will be used for Symmetric algorithms only (HMAC),
| since RSA and ECDSA use a private/public key combo (See below).
|
*/
'secret' => env('JWT_SECRET'),
/*
|--------------------------------------------------------------------------
| JWT Authentication Keys
|--------------------------------------------------------------------------
|
| The algorithm you are using, will determine whether your tokens are
| signed with a random string (defined in `JWT_SECRET`) or using the
| following public & private keys.
|
| Symmetric Algorithms:
| HS256, HS384 & HS512 will use `JWT_SECRET`.
|
| Asymmetric Algorithms:
| RS256, RS384 & RS512 / ES256, ES384 & ES512 will use the keys below.
|
*/
'keys' => [
/*
|--------------------------------------------------------------------------
| Public Key
|--------------------------------------------------------------------------
|
| A path or resource to your public key.
|
| E.g. 'file://path/to/public/key'
|
*/
'public' => env('JWT_PUBLIC_KEY'),
/*
|--------------------------------------------------------------------------
| Private Key
|--------------------------------------------------------------------------
|
| A path or resource to your private key.
|
| E.g. 'file://path/to/private/key'
|
*/
'private' => env('JWT_PRIVATE_KEY'),
/*
|--------------------------------------------------------------------------
| Passphrase
|--------------------------------------------------------------------------
|
| The passphrase for your private key. Can be null if none set.
|
*/
'passphrase' => env('JWT_PASSPHRASE'),
],
/*
|--------------------------------------------------------------------------
| JWT time to live
|--------------------------------------------------------------------------
| token 有效時間 分鐘數
| Specify the length of time (in minutes) that the token will be valid for.
| Defaults to 1 hour.
|
| You can also set this to null, to yield a never expiring token.
| Some people may want this behaviour for e.g. a mobile app.
| This is not particularly recommended, so make sure you have appropriate
| systems in place to revoke the token if necessary.
| Notice: If you set this to null you should remove 'exp' element from 'required_claims' list.
|
*/
'ttl' => env('JWT_TTL', 60),
/*
|--------------------------------------------------------------------------
| Refresh time to live
|--------------------------------------------------------------------------
| token 可被重新整理的分鐘數 ,超出後將無法呼叫介面繼續重新整理
| Specify the length of time (in minutes) that the token can be refreshed
| within. I.E. The user can refresh their token within a 2 week window of
| the original token being created until they must re-authenticate.
| Defaults to 2 weeks.
|
| You can also set this to null, to yield an infinite refresh time.
| Some may want this instead of never expiring tokens for e.g. a mobile app.
| This is not particularly recommended, so make sure you have appropriate
| systems in place to revoke the token if necessary.
|
*/
'refresh_ttl' => env('JWT_REFRESH_TTL', 20160),
/*
|--------------------------------------------------------------------------
| JWT hashing algorithm
|--------------------------------------------------------------------------
|
| Specify the hashing algorithm that will be used to sign the token.
|
| See here: https://github.com/namshi/jose/tree/master/src/Namshi/JOSE/Signer/OpenSSL
| for possible values.
|
*/
'algo' => env('JWT_ALGO', 'HS256'),
/*
|--------------------------------------------------------------------------
| Required Claims
|--------------------------------------------------------------------------
|
| Specify the required claims that must exist in any token.
| A TokenInvalidException will be thrown if any of these claims are not
| present in the payload.
|
*/
'required_claims' => [
'iss',
'iat',
'exp',
'nbf',
'sub',
'jti',
],
/*
|--------------------------------------------------------------------------
| Persistent Claims
|--------------------------------------------------------------------------
|
| Specify the claim keys to be persisted when refreshing a token.
| `sub` and `iat` will automatically be persisted, in
| addition to the these claims.
|
| Note: If a claim does not exist then it will be ignored.
|
*/
'persistent_claims' => [
// 'foo',
// 'bar',
],
/*
|--------------------------------------------------------------------------
| Lock Subject
|--------------------------------------------------------------------------
|
| This will determine whether a `prv` claim is automatically added to
| the token. The purpose of this is to ensure that if you have multiple
| authentication models e.g. `App\User` & `App\OtherPerson`, then we
| should prevent one authentication request from impersonating another,
| if 2 tokens happen to have the same id across the 2 different models.
|
| Under specific circumstances, you may want to disable this behaviour
| e.g. if you only have one authentication model, then you would save
| a little on token size.
|
*/
'lock_subject' => true,
/*
|--------------------------------------------------------------------------
| Leeway
|--------------------------------------------------------------------------
|
| This property gives the jwt timestamp claims some "leeway".
| Meaning that if you have any unavoidable slight clock skew on
| any of your servers then this will afford you some level of cushioning.
|
| This applies to the claims `iat`, `nbf` and `exp`.
|
| Specify in seconds - only if you know you need it.
|
*/
'leeway' => env('JWT_LEEWAY', 5),
/*
|--------------------------------------------------------------------------
| Blacklist Enabled
|--------------------------------------------------------------------------
|
| In order to invalidate tokens, you must have the blacklist enabled.
| If you do not want or need this functionality, then set this to false.
|
*/
'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true),
/*
| -------------------------------------------------------------------------
| Blacklist Grace Period
| -------------------------------------------------------------------------
|
| When multiple concurrent requests are made with the same JWT,
| it is possible that some of them fail, due to token regeneration
| on every request.
|
| Set grace period in seconds to prevent parallel request failure.
|
*/
'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 5),
/*
|--------------------------------------------------------------------------
| Cookies encryption
|--------------------------------------------------------------------------
|
| By default Laravel encrypt cookies for security reason.
| If you decide to not decrypt cookies, you will have to configure Laravel
| to not encrypt your cookie token by adding its name into the $except
| array available in the middleware "EncryptCookies" provided by Laravel.
| see https://laravel.com/docs/master/responses#cookies-and-encryption
| for details.
|
| Set it to true if you want to decrypt cookies.
|
*/
'decrypt_cookies' => false,
/*
|--------------------------------------------------------------------------
| Providers
|--------------------------------------------------------------------------
|
| Specify the various providers used throughout the package.
|
*/
'providers' => [
/*
|--------------------------------------------------------------------------
| JWT Provider
|--------------------------------------------------------------------------
|
| Specify the provider that is used to create and decode the tokens.
|
*/
'jwt' => Tymon\JWTAuth\Providers\JWT\Lcobucci::class,
/*
|--------------------------------------------------------------------------
| Authentication Provider
|--------------------------------------------------------------------------
|
| Specify the provider that is used to authenticate users.
|
*/
'auth' => Tymon\JWTAuth\Providers\Auth\Illuminate::class,
/*
|--------------------------------------------------------------------------
| Storage Provider
|--------------------------------------------------------------------------
|
| Specify the provider that is used to store tokens in the blacklist.
|
*/
'storage' => Tymon\JWTAuth\Providers\Storage\Illuminate::class,
],
];
建立授權模型
php artisan make:model Models\Doctor
php artisan make:model Models\Patient
調整模型
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Tymon\JWTAuth\Contracts\JWTSubject;
class Doctor extends Authenticatable implements JWTSubject
{
protected $guarded = [];
/**
* 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 [];
}
}
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Tymon\JWTAuth\Contracts\JWTSubject;
class Patient extends Authenticatable implements JWTSubject
{
protected $guarded = [];
/**
* 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 [];
}
}
在 config/auth.php 檔案中配置 guard
<?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' => 'web',
'passwords' => 'users',
],
/*
|--------------------------------------------------------------------------
| 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' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'doctor' => [
'driver' => 'jwt',
'provider' => 'doctors',
],
'patient' => [
'driver' => 'jwt',
'provider' => 'patients',
],
],
/*
|--------------------------------------------------------------------------
| 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\Models\User::class,
],
'doctors' => [
'driver' => 'eloquent',
'model' => App\Models\Doctor::class,
],
'patients' => [
'driver' => 'eloquent',
'model' => App\Models\Patient::class,
],
],
/*
|--------------------------------------------------------------------------
| Resetting Passwords
|--------------------------------------------------------------------------
|
| 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' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
'throttle' => 60,
],
],
/*
|--------------------------------------------------------------------------
| Password Confirmation Timeout
|--------------------------------------------------------------------------
|
| Here you may define the amount of seconds before a password confirmation
| times out and the user is prompted to re-enter their password via the
| confirmation screen. By default, the timeout lasts for three hours.
|
*/
'password_timeout' => 10800,
];
登入控制器
<?php
namespace App\Http\Controllers;
use App\Models\Doctor;
use App\Models\Patient;
use Illuminate\Http\Request;
class AuthController extends Controller
{
function doctorLogin(Request $request){
$oDoctor = Doctor::query()->first();
// 手動認證使用者
$token = auth('doctor')->login($oDoctor);
return $this->success(['token' => 'Bearer '.$token], '登入成功');
}
function patientLogin(Request $request){
$oPatient = Patient::query()->first();
// 手動認證使用者
$token = auth('patient')->login($oPatient);
return $this->success(['token' => 'Bearer '.$token], '登入成功');
}
public function logout(Request $request){
// 退出登入
auth('doctor')->logout();
auth('patient')->logout();
auth()->logout();
}
}
路由
<?php
use Illuminate\Routing\Router;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::group(
[
'prefix' => 'doctors',
],
function(Router $router){
Route::post('login','AuthController@doctorLogin');
$router->group(
[
'middleware' => 'auth.jwt:doctor'
],
function(Router $router){
$router->get('test',function (){
dd(auth()->user());
});
});
}
);
Route::group(
[
'prefix' => 'patients',
],
function(Router $router){
Route::post('login','AuthController@patientLogin');
$router->group(
[
'middleware' => 'auth.jwt:patient'
],
function(Router $router){
$router->get('test',function (){
dd(auth()->user());
});
});
}
);
認證校驗及自動重新整理中介軟體
<?php
namespace App\Http\Middleware;
use App\Traits\JsonResponse;
use Closure;
use Illuminate\Support\Facades\Auth;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenBlacklistedException;
use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
class CheckAndRefreshToken extends BaseMiddleware
{
use JsonResponse;
/**
* @param $request
* @param Closure $next
* @param $guard
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\JsonResponse|\Illuminate\Http\Response|mixed|\Symfony\Component\HttpFoundation\Response
* @throws JWTException
*/
public function handle($request, Closure $next, $guard)
{
try{
auth()->shouldUse($guard);
// 檢查 jwt token 令牌
$this->checkForToken($request);
// 驗證簽發 token 來源
if(!strstr(auth($guard)->payload()->get('iss'),$guard)){
return $this->error('未登入',[],401);
}
// 認證校驗
if(!auth($guard)->check()){
return $this->error('未登入',[],401);
}
$sToken = auth($guard)->setTTL(config('jwt.ttl'))->getToken();
}catch (TokenExpiredException $exception){
// 處理重新整理 token 的異常
try {
// token 過期
$sToken = auth($guard)->refresh();
Auth::guard($guard)->onceUsingId($this->auth->manager()->getPayloadFactory()->buildClaimsCollection()->toPlainArray()['sub']);
}catch (JWTException $exception) {
// token 令牌 重新整理時間超時,或使用失效的 token 重新整理
return $this->error('登入超時請重新登入',[],401);
}
}catch (TokenBlacklistedException $exception){
// token 令牌被拉黑
return $this->error('令牌無效',[],401);
}catch (TokenInvalidException $exception){
// token 令牌無效
return $this->error('令牌無效',[],401);
}catch (JWTException $exception){
// token 令牌超出重新整理時間
return $this->error('登入超時請重新登入',[],401);
}
// 在響應頭中返回新的 token
return $this->setAuthenticationHeader($next($request), $sToken);
}
}
PS: 使用 jwt token 中的來源來區分多guard
更多參考:
本作品採用《CC 協議》,轉載必須註明作者和本文連結