10. Laravel api 认证
Laravel 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 中的 storage_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,这会使得你的 BaseRequest 继承 FormRequest,而我们要覆盖的是 Illuminate\Http\Request,所以我们应当继承的是 Request。
新建文件 app\Http\Requests\BaseRequest.php:
<?php
namespace App\Http\Requests;
use Illuminate\Http\Request;
class BaseRequest extends Request
{
public function expectsJson()
{
return true;
}
public function wantsJson()
{
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')
middleware('auth:api')
# auth:api,editer 就表示 auth 中间件的 handle 方法的第三个参数值是 api, 第四个参数值是 editer。
# Auth 中间件来说,参数值 api 表示所使用的 guard 是 api
// Illuminate\Auth\Middleware\Authenticate->handle()
public function handle($request, Closure $next, ...$guards)
{
$this->authenticate($request, $guards);
return $next($request);
}
# 此处api与config/auth.php 配合使用,默认是web 用可以路由中用auth:api指定为api的配置 修改默认后可以用auth
给 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',
],
]);