Laravel 通过 Passport 实现 API 请求认证

标签: 编程学习 PHP学习

在处理博客API,目前正在整理用户认证这一块,方式有很多种,我这里选择的是用 Passport 来实现,这是一个官方提供的基于 OAuth 2.0 的扩展包。

安装扩展包

使用 Composer 来安装 Passport 扩展包:

composer require laravel/passport

生成所需数据库表:

php artisan migrate

接下来运行如下 Artisan 生成秘钥及数据:

php artisan passport:install

该命令会在 storage 目录下生成 oauth-private.key 和 oauth-public.key 两个秘钥文件,用于安全令牌的加密解密, 然后在 oauth_clients 数据表中初始化两条记录,相当于注册了两个客户端应用,一个用于密码授权令牌认证,一个用于私人访问令牌认证。

模型及路由支持

如果要让用户支持 API 认证,需要在对应模型类中使用 Laravel\Passport\HasApiTokens Trait,里面包含了认证所需的方法,我这里只是用户类用到,所以需要修改 User 类:

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Passport\HasApiTokens;

class User extends Authenticatable {
    use HasApiTokens, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [];
}

Passport 为 API 认证提供了相应的路由,我们需要在 AuthServiceProvider 的 boot 方法中注册 API 认证相关路由:

// 顶部引入相应命名空间
use Laravel\Passport\Passport;

...

public function boot() {
    ...

    // API 认证路由注册
    Passport::routes();
}

将 config/auth.php 中的 API 认证驱动由 token 修改为 Passport:

'guards' => [
    ...
    'api' => [
        'driver' => 'passport',
        'provider' => 'users',
    ],
],

添加路由配置

Route::group(['namespace' => 'Api\v1', 'prefix' => 'v1'], function () {
    //验证码,比较穷,所以暂时选择邮箱验证码
    Route::get('sendCode', 'AuthController@sendCodeAction');
    //登录相关
    Route::post('login', 'AuthController@loginAction');
});

//API认证采用passport
Route::group(['namespace' => 'Api\v1', 'prefix' => 'v1', 'middleware' => 'auth:api'], function () {
    Route::post('user', 'UserController@index');
});

实现登录

直接使用 Passport 提供的方法 createToken 创建 token

/// 登录
public function loginAction(Request $request) {

    $this->validateLogin($request);

    if ($this->hasTooManyLoginAttempts($request)) {
        $this->fireLockoutEvent($request);
        return $this->sendLockoutResponse($request);
    }

    $credentials = $this->credentials($request);

    if ($this->guard()->attempt($credentials)) {
        //获取邮箱
        $name = $request->get('username');
        //删除当前用户之前的token
        DB::table('oauth_access_tokens')->where('name', $name)->delete();
        //添加新的token,token名字为手机号
        $token = \Auth::getUser()->createToken($name)->accessToken;
        //返回token
        return ApiResponseBuilder::success($token);
    } else {
        return ApiResponseBuilder::errorWithMessage(200,'登录失败,请检查您的账号和密码',200);
    }
}

然后我们就可以直接访问到用户信息了

/**
 * 获取oauth认证之后的用户ID
 * @return mixed
 */
public function getUserId() {
    $user = \Auth::user();
    if(!$user){
        return ApiResponseBuilder::errorWithMessage(200, "error", 200);
    }
    return $user->getAuthIdentifier();
}

public function index(Request $request) {
    try {
        $id = $request->userid;
        if(!$id){
            $id = $this->getUserId();
        }
        $user = User::findOrFail($id);
        return ApiResponseBuilder::success($user);
    } catch (\Exception $e) {
        return ApiResponseBuilder::errorWithMessage(200, "数据丢失", 200);
    }
}

这里要注意,拼接 Authorization 的时候中间是两个空格,之前被这个坑过