歡迎閱讀該文章!
- 本教程使用laravel提供的auth元件來做的認證登入
- 本文教程只需要你安裝了基本的laravel執行環境即可執行
一. 安裝
安裝tymon/jwt-auth
,程式碼如下:
composer require tymon/jwt-auth ^1.0 // 安裝
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider" // 生成配置檔案
php artisan jwt:secret // 初步金鑰生成
二. 配置
認證配置修改
修改你的config/auth.php
檔案,如下:
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users',
'hash' => false,
],
],
修改你的App/User.php
檔案,如下:
<?php
namespace App;
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable implements JWTSubject
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
/**
* 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 [];
}
}
這裡解析下:
laravel中的預設使用web
來進行認證的守衛,這裡 jwt 主要是弄介面的嘛,所以自然而然的改成 api。守衛中的api
驅動也改成我們們下載下來即將要用到的jwt
。這樣laravel的認證配置就大功告成了。
資料的初步生成
使用laravel的提供的auth,如下:
php artisan make:auth
- 好了,這是在
database/migrations
檔案中生成了即將要遷移的使用者資料表。 - 有了資料表還不夠,我們們還需要往裡面填充資料。執行
php artisan make:seeder UsersTableSeeder
命令在database/seeds
資料夾中會生成UsersTableSeeder.php
檔案用來填充資料。該檔案程式碼如下:use Illuminate\Database\Seeder; use App\User; use Illuminate\Contracts\Hashing\Hasher as HasherContract; class UsersTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run(HasherContract $hasher) { $arr = [ [ 'name' => 'jack', 'email' => '775893055@qq.com', 'password' => $hasher->make('123456'), ], [ 'name' => 'hurry', 'email' => '1041224389@qq.com', 'password' => $hasher->make('123456'), ], ]; User::insert($arr); } }
- 然後記得把
seeds\DatabaseSeeder.php
中的註釋去掉<?php use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder { /** * Seed the application's database. * * @return void */ public function run() { $this->call(UsersTableSeeder::class); } }
- 在
.env
檔案中配置好你的資料庫,執行:php artisan migrate --seed
- 路由配置,檔案
routes\api.php
,如下:<?php use Illuminate\Http\Request; Route::middleware('auth:api')->get('/user', function (Request $request) { return $request->user(); }); Auth::routes();
三. 程式碼編寫
Auth\LoginController.php
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* @var string
*/
protected $redirectTo = '/home';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
/**
* 重寫登入方法(jwt)
*
* @param Request $request
* @return mixed
* @throws \Illuminate\Validation\ValidationException
*/
public function login(Request $request)
{
$this->validateLogin($request);
$credentials = $this->credentials($request);
if (!$token = auth()->attempt($credentials)) {
return $this->failed('登入失敗,賬號或者密碼錯誤', '401');
}
return $this->success([
'access_token' => $token,
'token_type' => 'bearer',
'user' => auth()->user(),
'expires_in' => auth()->factory()->getTTL() * 60
]);
}
}
Exceptions\Handler.php
<?php
namespace App\Exceptions;
use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use App\Http\Controllers\Controller;
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that are not reported.
*
* @var array
*/
protected $dontReport = [
//
];
/**
* A list of the inputs that are never flashed for validation exceptions.
*
* @var array
*/
protected $dontFlash = [
'password',
'password_confirmation',
];
/**
* Report or log an exception.
*
* @param \Exception $exception
* @return void
*/
public function report(Exception $exception)
{
parent::report($exception);
}
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $exception
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
if ($request->is("api/*")) {
if ($exception instanceof NotFoundHttpException) {
return (new Controller())->failed('路由沒找到.');
}
if ($exception instanceof ValidationException) {
return (new Controller())->failed($exception->validator->errors()->first());
}
if ($exception->getPrevious() instanceof TokenExpiredException) {
return (new Controller())->failed('登入超時,請重新登入', 401);
}
return (new Controller())->failed($exception->getMessage());
}
return parent::render($request, $exception);
}
}
Controllers\Controllers.php
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Response;
use Illuminate\Support\Facades\DB;
use Symfony\Component\HttpFoundation\Response as FoundationResponse;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
/**
* @var int
*/
protected $statusCode = FoundationResponse::HTTP_OK;
/**
* 獲取響應http狀態碼
*
* @return mixed
*/
public function getStatusCode()
{
return $this->statusCode;
}
/**
* 設定響應http狀態碼
*
* @param $statusCode
* @return $this
*/
public function setStatusCode($statusCode)
{
$this->statusCode = $statusCode;
return $this;
}
/**
* 封裝的底層http響應
*
* @param $data
* @param array $header
* @return mixed
*/
public function respond($data, $header = [])
{
return Response::json($data, $this->getStatusCode(), $header);
}
/**
* 封裝status
*
* @param $status
* @param array $data
* @param null $code
* @return mixed
*/
public function status($status, array $data, $code = null)
{
if ($code) {
$this->setStatusCode($code);
}
$status = [
'status' => $status,
'code' => $this->statusCode
];
if (config('app.debug')) {
$status['logs'] = DB::getQueryLog();
}
$data = array_merge($status, $data);
return $this->respond($data);
}
/**
* 訊息響應
*
* @param $message
* @param string $status
* @return mixed
*/
public function message($message, $status = "success")
{
return $this->status($status, [
'message' => $message
]);
}
/**
* 建立成功響應
*
* @param string $message
* @return mixed
*/
public function created($message = "created")
{
return $this->setStatusCode(FoundationResponse::HTTP_CREATED)
->message($message);
}
/**
* 通用返回
*
* @param $data
* @return mixed
*/
public function returnMsg($data)
{
if ($data['success'] == true) {
if ($data['data']) {
return $this->success($data['data']);
}
return $this->message($data['msg']);
} else {
return $this->failed($data['msg']);
}
}
/**
* 成功響應
*
* @param $data
* @param string $status
* @return mixed
*/
public function success($data, $status = "success")
{
if (isset($data['success']) && $data['success'] === false) {
return $this->returnMsg($data);
}
return $this->status($status, compact('data'));
}
/**
* 失敗響應
*
* @param $message
* @param int $code
* @param string $status
* @return mixed
*/
public function failed($message = '操作失敗', $code = FoundationResponse::HTTP_BAD_REQUEST, $status = 'error')
{
return $this->setStatusCode($code)->message($message, $status);
}
/**
* HTTP內部伺服器錯誤
*
* @param string $message
* @return mixed
*/
public function internalError($message = "Internal Error!")
{
return $this->failed($message, FoundationResponse::HTTP_INTERNAL_SERVER_ERROR);
}
/**
* 不存在的api
*
* @param string $message
* @return mixed
*/
public function notFond($message = 'Not Fond!')
{
return $this->failed($message, FoundationResponse::HTTP_NOT_FOUND);
}
/**
* 介面未授權
*
* @param string $message
* @return mixed
*/
public function unAuthorized($message = "Unauthorized!")
{
return $this->failed($message, FoundationResponse::HTTP_UNAUTHORIZED);
}
/**
* 沒有許可權操作指定資源
*
* @param string $message
* @return mixed
*/
public function forbidden($message = "Forbidden!")
{
return $this->failed($message, FoundationResponse::HTTP_FORBIDDEN);
}
}
好了,完成上面程式碼的編寫之後,我們們就可以登入了。訪問介面/api/login
,進行登入:
emmmm,可以看到有返回到的token。接下來就把這個token帶上頭部就可以進行使用者的認證了。
具體的方法是 Authorization: bearer +你的token,結合下面的中間鍵使用。基本滿足使用者認證,單點登入的需求了啦。
四. 中間鍵
Http\Kernel.php
檔案中$routeMiddleware
陣列:加上一行:
'jwt.auth' => \Tymon\JWTAuth\Http\Middleware\AuthenticateAndRenew::class,
這樣,你就可以在需要認證的路由上面加上這個中間鍵就可以了。
五. 獲取當前認證使用者的方法
auth()->user() 或 $request->user()
好了。差不多就這樣了。這裡涵蓋了laravel中api的基本搭建。
為了方便大家的理解:我弄了哥小小的demo放上了github上面:
最後,如果發現有什麼問題,錯誤的,私信我。儘快修正。
努力,加油。