Laravel学习笔记(24)laravel6 认证与授权(api)

原文:https://qianjinyike.com/laravel-api-%E8%AE%A4%E8%AF%81/

编写完全为 API 服务的 Laravel 应用,并所有响应都是 JSON 格式时,使用API认证

  1. 原理

注册:用户注册成功后,随机生成长字符串作为 token,原生 token 返回给用户cookie。哈希后的 token 存到数据库里。
登陆:用户使用账号密码登陆成功,随机生成长字符串作为 token,原生 token 返回给用户cookie。哈希后的 token 存到数据库里。
认证:将用户传来的 token 进行哈希,然后去数据库中查找之前存的哈希过的token进行对比 ,相等就认证成功,否则失败

  1. 数据库追加api_token字段并在User模型中添加白名单
$table->string('api_token', 80)->unique()->nullable()->default(null);
  1. 修改 api_token 这个名称
// 如果修改字段名称 api_token,请记得修改配置文件 config/auth.php 中的 storage_key

'api' => [
    'driver' => 'token',
    'provider' => 'users',
    'hash' => false,
    'storage_key' => 'api_token',
],
  1. 修改配置,将默认web认证改为API认证

guard 设置为 api,hash 设置为 true

// config/auth.php

'defaults' => [
    'guard' => 'api',     // 默认 api 认证
    'passwords' => 'users',
],

'api' => [
    'driver' => 'token',
    'provider' => 'users',
    'hash' => true,       // 利用 SHA-256 算法哈希你的令牌
],
  1. 编写 BaseRequest
    首先我们需要构建一个 BaseRequest 来重写 Illuminate\Http\Request ,修改为默认优先使用 JSON 响应:
app/Http/Requests/BaseRequest.php

<?php
namespace App\Http\Requests;

use Illuminate\Http\Request;

// 注意 继承于Request而不是FormRequest,所以不能直接用artisan make:request生成
class BaseRequest extends Request
{
    public function expectsJson()
    {
        return true;
    }
    public function wantsJson()
    {
        return true;
    }
}
  1. 替换 BaseRequest
// 在 public/index.php 文件中,将 \Illumiate\Http\Request 替换为我们的 BaseRequest,如下:

$response = $kernel->handle(
    $request = \App\Http\Requests\BaseRequest::capture()
);

PS:第五步和第六步执行后,所有的响应都是 application/json ,包括错误和异常

  1. Json的返回方法

方式一:使用 json() 方法#

return response()->json([ 
    'name' => 'Abigail', 
    'state' => 'CA' 
]);

json() 方法可以将 Content-Type 头设置为 application/ json,同时利用 PHP 内置函数 json_encode,把数组转成 JSON 格式。

方式二:直接返回数组

$arr = [1,2];
return $arr;

这种方法返回的 Content-Type 是 text/html。

上述两种方式的区别:
方式一 http Response Headers 的 Content-Type 是 application/json。
方式二 http Response Headers 的 Content-Type 是 text/html。

  1. 生成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');
    }

    // 默认使用email进行验证
    protected function username()
    {
        // return 'name';
        return 'email';
    }

	// 注册的时候生成一个哈希过的api_token
    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'],
        ]);
//        等效于
//        $request->validate([
//            ....
//        ])
    }

    protected function create(array $data)
    {
        // 实际上,属性值是否可以批量赋值需要受 fillable 或 guarded 来控制,如果我们想要强制批量赋值可以使用 forceCreate
        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()
    {
        // web登出是Auth::logout()
        auth()->user()->update(['api_token' => null]);

        return ['message' => '退出登录成功'];
    }

	// 先进行登录验证,之后重新生成一个哈希过的api_token并更新User表
    public function login()
    {
        $user = User::where($this->username(), request($this->username()))
            ->firstOrFail();

        // 将request('password')哈希后与$user->password进行比对
        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');
    }

	// 重新生成一个哈希过的api_token并更新User表
    public function refresh()
    {
        $api_token = Str::random(80);
        auth()->user()->update(['api_token' => hash('sha256', $api_token)]);

        return compact('api_token');
    }
}
  1. 路由
Route::post('/register', 'Auth\ApiController@register');
Route::post('/login', 'Auth\ApiController@login');
Route::post('/refresh', 'Auth\ApiController@refresh');
Route::post('/logout', 'Auth\ApiController@logout');
  1. 过期时间

此时的token是没有过期时间的,永久存在

如果要让token有过期时间,就要在表中加入过期时间字段,然后新建一个中间件,在中间件里判断当前时间是否大于过期时间,如果大于的话,就认证失败。

发布了40 篇原创文章 · 获赞 0 · 访问量 767

猜你喜欢

转载自blog.csdn.net/qj4865/article/details/104333654