api 認證
原理
- 註冊:用戶註冊成功後,隨機生成長字符串作爲 token,原生 token 返回給用戶。哈希後的 token 存到數據庫裏。
- 登陸:用戶使用賬號密碼登陸成功,隨機生成長字符串作爲 token,原生 token 返回給用戶。哈希後的 token 存到數據庫裏。
- 認證:將用戶傳來的 token 進行哈希,然後取數據庫中查找哈希後的 token ,找到了就認證成功,否則失敗。
創建項目與配置
composer create-project --prefer-dist laravel/laravel laravel6
php artisan migrate
添加 api_token 字段,可空,唯一,默認 null。(可直接修改,也可以創建下面的代碼片段然後遷移)
Schema::table('users', function ($table) {
$table->string('api_token', 80)->after('password')
->unique()
->nullable()
->default(null);
});
php artisan migrate
我們的例子還需要設置 email 可爲空,因爲我們以用戶名作爲認證的依據
設置模型可以操作 api_token 字段
# App\User.php
protected $fillable = [
'name', 'email', 'password', 'api_token',
];
修改 api_token 這個名稱
如果修改字段名稱 api_token,請記得改配置文件 config/auth.php 中的 stroage_key
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
'storage_key' => 'api_token',
],
guard 設置爲 api,hash 設置爲 true
// config/auth.php
'defaults' => [
'guard' => 'api', // 默認 api 認證
'passwords' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => true, // 利用 SHA-256 算法哈希你的令牌
],
設置所有請求和響應都是 json 格式
php artisan make:request BaseRequest
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class BaseRequest extends FormRequest
{
public function wantsJson(){
return true;
}
public function expectsJson(){
return true;
}
}
// index.php
$response = $kernel->handle(
$request = \App\Http\Requests\BaseRequest::capture()
);
編寫 api 認證代碼
Route::post('/register', 'Auth\ApiController@register');
Route::post('/login', 'Auth\ApiController@login');
Route::post('/refresh', 'Auth\ApiController@refresh');
Route::post('/logout', 'Auth\ApiController@logout');
php artisan make:controller Auth\ApiController
<?php
// email 設置可爲空
// request 和 response 都是 json 格式
// api_token 設置可插入數據庫
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
class ApiController extends Controller
{
public function __construct()
{
$this->middleware('auth')->except('login', 'register');
}
protected function username()
{
return 'name';
}
public function register(Request $request)
{
$this->validator($request->all())->validate();
$api_token = Str::random(80);
$data = array_merge($request->all(), compact('api_token'));
$this->create($data);
return compact('api_token');
}
protected function validator(array $data)
{
return Validator::make($data, [
'name' => ['required', 'string', 'max:255', 'unique:users',],
// 'email' => ['required', 'string', 'email', 'max:255',],
'password' => ['required', 'string', 'min:8', 'confirmed'],
]);
}
protected function create(array $data)
{
return User::forceCreate([
'name' => $data['name'],
// 'email' => $data['email'],
'password' => password_hash($data['password'], PASSWORD_DEFAULT),
'api_token' => hash('sha256', $data['api_token']),
]);
}
public function logout()
{
auth()->user()->update(['api_token' => null]);
return ['message' => '退出登錄成功'];
}
public function login()
{
$user = User::where($this->username(), request($this->username()))
->firstOrFail();
if (!password_verify(request('password'), $user->password)) {
return response()->json(['error' => '抱歉,賬號名或者密碼錯誤!'],
403);
}
$api_token = Str::random(80);
$user->update(['api_token' => hash('sha256', $api_token)]);
return compact('api_token');
}
public function refresh()
{
$api_token = Str::random(80);
auth()->user()->update(['api_token' => hash('sha256', $api_token)]);
return compact('api_token');
}
}
保護路由
middleware('auth:api')
給 Request 傳 token
$response = $client->request('GET', '/api/user?api_token='.$token);
$response = $client->request('POST', '/api/user', [
'headers' => [
'Accept' => 'application/json',
],
'form_params' => [
'api_token' => $token,
],
]);
$response = $client->request('POST', '/api/user', [
'headers' => [
'Authorization' => 'Bearer '.$token,
'Accept' => 'application/json',
],
]);