在做前后臺分離的項目中缓淹,認證是必須的,由于http是無狀態(tài)的。前臺用戶登錄成功后割卖,后臺給前臺返回token前酿。之后前臺給后臺發(fā)請求每次攜帶token。
原理也非常簡單:
-
前天在請求頭中添加 Authorization鹏溯,如下
image.png - 后臺取到值罢维,然后去用戶表的api_token列進行匹配,如果查到說明驗證成功丙挽,并且返回相關(guān)信息肺孵。
Laravel本身自帶幾種驗證方式,下面介紹下token認證的實現(xiàn)的方法颜阐。
前臺在向后臺發(fā)起請求時要攜帶一個token
后臺需要做一個返回當(dāng)前登錄用戶的信息的api平窘,地址是 /api/user
- 先添加路由,當(dāng)給 route/api.php 添加
Route::middleware('auth:api')->get('/user', function (Request $request) {
echo $request->user();
});
如果瀏覽器直接訪問 http://mydomain.com/api/user
會返回 401 Unauthorized
原因是在config/auth.php中有下面的關(guān)鍵配置
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
],
可以看到通過api訪問走的是token認證凳怨,這里沒有提供token所以就認證失敗返回401了瑰艘。
-
'driver' => 'token'
實際調(diào)用的是\vendor\laravel\framework\src\Illuminate\Auth\TokenGuard.php
上面說到我們需要在request里提供api_token參數(shù),為了區(qū)別是哪個用戶肤舞,需要在user表添加api_token字段
- 認證過程調(diào)用的是getTokenForRequest方法
public function getTokenForRequest()
{
$token = $this->request->query($this->inputKey);
if (empty($token)) {
$token = $this->request->input($this->inputKey);
}
if (empty($token)) {
$token = $this->request->bearerToken();
}
if (empty($token)) {
$token = $this->request->getPassword();
}
return $token;
}
這個bearerToken實際找header中是否存在Authorization
public function bearerToken()
{
$header = $this->header('Authorization', '');
if (Str::startsWith($header, 'Bearer ')) {
return Str::substr($header, 7);
}
}
- 先給user表添加api_token字段
php artisan make:migration add_api_token_to_users --table=users
內(nèi)容
class AddApiTokenToUsers extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('api_token', 60)->unique();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('api_token');
});
}
}
-
打開navicat進到user表里紫新,更新users的api_token。
image.png -
打開postman
image.png
注意這里的header李剖,key是Authorization芒率,值就是Bearer+空格+剛才數(shù)據(jù)庫里設(shè)的api_token
這樣就能返回內(nèi)容啦,修改其他用戶的token能返回相應(yīng)的用戶信息篙顺,說明認證成功偶芍,功能基本完成!
下面完善細節(jié)
- 完善邏輯
修改\app\Http\Controllers\Auth\RegisterController.php
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
// 添加這行
'api_token' => str_random(60),
]);
}
User Model 的 $fillable也改下
protected $fillable = [
'name', 'email', 'password', 'api_token',
];
- 如果在前臺頁面德玫,發(fā)起請求時如何給后臺傳這個Authorization header? 方法如下
注意匪蟀,下面的是Laravel5.4的修改方法。新版本可能有細微區(qū)別化焕,只要知道原理就能自己改了萄窜。
打開 \resources\assets\js\bootstrap.js
參照著csrf-token铃剔。合適的地方添加下面的代碼
let token = document.head.querySelector('meta[name="csrf-token"]');
let api_token = document.head.querySelector('meta[name="api-token"]');
if (token) {
// 這個要參考axios的文檔
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = Laravel.csrfToken =token.content;
// 如果用的jquery
// Fix jquery ajax crossDomain without Token
// jQuery.ajaxPrefilter(function (options, originalOptions, jqXHR) {
// // if (options.crossDomain) {
// jqXHR.setRequestHeader('Authorization', api_token.content);
// jqXHR.setRequestHeader('X-CSRF-TOKEN', token.content);
// //}
// });
} else {
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
if (api_token) {
window.axios.defaults.headers.common['Authorization'] = api_token.content;
} else {
console.error('Authorization token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
最后修改公共視圖模版中 \views\layouts\app.blade.php
<meta name="csrf-token" content="{{ csrf_token() }}">
<meta name="api-token" content="{{ Auth::check() ? 'Bearer '.Auth::user()->api_token : 'Bearer ' }}">
總結(jié):
本質(zhì)上給用戶表添加api_token撒桨,后臺根據(jù)這個字段判斷是否是有效的用戶,無效返回401键兜,有效返回查詢結(jié)果凤类。
優(yōu)點是容易理解,缺點太簡單普气,安全也不夠谜疤。
為了安全,可以實現(xiàn)下面的功能:
- 每次登錄成功后刷新api_token為新值
其實 Laravel 官方提供了一個 Laravel Passport 的包。Laravel Passport is an OAuth2 server and API authentication package 夷磕。
具體使用請等更新履肃。
問題:
如何修改默認的api_token列?