在 config/auth.php 裡面就已經描述好兩種認證方式:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
],
如果我們建立 project 之後做了 make:auth 就直接建立 users 資料表的話。
不管怎麼樣 Route::middleware('auth:api') 都是回應 401 認證不會過的。
可以先看到 vendor/laravel/framework/src/Illuminate/Auth/TokenGuard.php 有描述。
public function __construct(UserProvider $provider, Request $request)
{
$this->request = $request;
$this->provider = $provider;
$this->inputKey = 'api_token';
$this->storageKey = 'api_token';
}
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;
}
$user = null;
$token = $this->getTokenForRequest();
if (! empty($token)) {
$user = $this->provider->retrieveByCredentials(
[$this->storageKey => $token]
);
}
return $this->user = $user;
}
$token = 這邊會從 request 那裡面找附帶的 token
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;
}
找尋 token 的順序為:
1. 從 query 找 $this->inputKey 參數
2. 從 input 找 $this->inputKey 參數
3. 從 header 找 bearer token
4. 從 header 找 password。註 1
1 和 2 很容易處理的,只要在送 request 之前把參數加一加就可以。
以 1 為例,我們只 url 加上 api_token 這個參數即可。
http://lara.loc/api/v1/stock?api_token=97531
回應的結果:
出現的是 QueryException,原因是 users 的資料表裡面沒有 api_token 這個欄位。
到 users 的資料表裡面加上這個欄位,並且賦予一個 unique 的值就可以了。
Schema::table('users', function (Blueprint $table) {
$table->string('api_token', 60)->unique();
});
app/Http/Controllers/Auth/RegisterController.php
在 User::create 的時候直接亂數產生一個 api_token 給他。
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'api_token' => str_random(60),
'password' => bcrypt($data['password']),
]);
}
並且記得在 app/User.php 裡面,在 $fillable 添加。
protected $fillable = [
'name', 'email', 'password', '
api_token',
];
之後註冊的使用者就會自動產生一組 api_token。
最後再回到我們送出 request 的部分:
axios.post('/api/v1/stock?
api_token={{ Auth::user()->api_token }}', form)
.then(function (response) {
console.log(response);
});
就可以順利通過認證。
也可以把 api_token 放在 header 裡面傳送:
axios.defaults.headers.common['Authorization'] = 'Bearer {{ Auth::user()->api_token }}';
axios.post('/api/v1/stock', form)
.then(function (response) {
console.log(response);
});
註 1
vendor/symfony/http-foundation/Request.php
/**
* Returns the password.
*
* @return string|null
*/
public function getPassword()
{
return $this->headers->get('PHP_AUTH_PW');
}
參考資料:
https://gist.github.com/gaillafr/02dc370c120062cf5f23896465f65fd9