企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
## **简介** 前面我们介绍的用户认证都是基于 Web 请求路由的,本质上都是基于 Session 实现的用户认证。在前后端分离大行其道的时代(这里提到的前后端分离包括前端与后端、App与后端、小程序与后端),基于 API 请求的认证也非常常见,但是我们知道常见的 Session 技术都是结合客户端 Cookie 来实现的,从后端剥离出去的前端应用无法通过 API 请求从客户端传递 Cookie 及 CSRF Token 到后端,也就无法通过 Session 实现用户认证。所以,我们需要寻求其它解决方案。 在 Laravel 中实现 API 认证有多种方式(例如[Passport](https://xueyuanjun.com/post/8298.html)),但本节会使用一个非常简化的方式。 ## **新增api_token字段** 首先添加`api_token`到`users`表: ~~~ php artisan make:migration --table=users adds_api_token_to_users_table ~~~ 然后编写并运行这个迁移文件: ~~~ class AddsApiTokenToUsersTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('users', function (Blueprint $table) { $table->string('api_token', 60)->unique()->nullable(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('users', function (Blueprint $table) { $table->dropColumn(['api_token']); }); } } ~~~ ## **创建注册接口** 我们使用`RegisterController`来根据注册请求返回正确的响应。尽管 Laravel 开箱提供了认证功能,但是我们还是需要对其进行调整以便返回我们想要的响应数据。 我们只需要在`RegisterController`中实现`registered`方法即可。该方法接收`$request`和`$user`参数: ~~~ protected function registered(Request $request, $user) { $user->generateToken(); return response()->json(['data' => $user->toArray()], 201); } ~~~ 在`routes/api.php`中注册路由如下: ~~~ Route::post('register', 'Auth\RegisterController@register'); ~~~ 在上面的示例代码中,我们调用了`User`模型上的生成令牌方法,该方法现在不存在,需要手动添加: ~~~ public function generateToken() { $this->api_token = str_random(60); $this->save(); return $this->api_token; } ~~~ 至此,注册接口编写完成,用户现在可以通过注册接口进行注册了,感谢 Laravel 开箱提供的认证字段验证功能,如果你需要调整验证规则的话可以到`RegisterController`中查看`validator`方法。 ## **创建登录接口** 和注册接口一样,可以编辑`LoginController`控制器来支持 API 认证。为此,我们需要在`LoginController`覆盖`AuthenticatesUsers`trait 提供的`login`方法: ~~~ public function login(Request $request) { $this->validateLogin($request); if ($this->attemptLogin($request)) { $user = $this->guard()->user(); $user->generateToken(); return response()->json([ 'data' => $user->toArray(), ]); } return $this->sendFailedLoginResponse($request); } ~~~ 然后在`routes/api.php`中注册登录路由: ~~~ Route::post('login', 'Auth\LoginController@login'); ~~~ ![](https://img.kancloud.cn/ae/fa/aefad498c0290f456643663bad58981c_2242x660.jpg) 后面就可以拿着这个`api_token`作为令牌来请求需要认证的资源了。使用我们现有的策略,请求认证资源时,如果没有 token 或 token 错误,用户将会接收到未认证响应(401)。 ## **创建退出接口** 为了形成完整闭环,下面我们来编写退出登录接口,实现思路是用户发起退出登录请求时,我们将其对应的 token 字段值从数据库移除。 首先,在`routes/api.php`中注册路由: ~~~ Route::post('logout', 'Auth\LoginController@logout'); ~~~ 然后在`Auth\LoginController.php`中编写`logout`方法: ~~~ public function logout(Request $request) { $user = Auth::guard('api')->user(); if ($user) { $user->api_token = null; $user->save(); } return response()->json(['data' => 'User logged out.'], 200); } ~~~ 使用该策略,一旦退出,用户的所有令牌都会失效,访问需要认证的接口都会拒绝访问(通过中间件实现),这需要和前端配合来避免用户在没有访问任何内容的权限下保持登录状态。 ## **使用中间件限制访问** `api_token`创建之后,我们就可以在路由文件中应用认证[中间件](https://xueyuanjun.com/post/7812.html)了: ~~~ Route::middleware('auth:api') ->get('/user', function (Request $request) { return $request->user(); }); ~~~ 我们可以使用`$request->user()`或`Auth`门面访问当前用户: ~~~ Auth::guard('api')->user(); // 登录用户实例 Auth::guard('api')->check(); // 用户是否登录 Auth::guard('api')->id(); // 登录用户ID ~~~ 接下来,我们将之前定义的文章相关路由进行分组: ~~~ Route::group(['middleware' => 'auth:api'], function() { Route::get('articles', 'ArticleController@index'); Route::get('articles/{article}', 'ArticleController@show'); Route::post('articles', 'ArticleController@store'); Route::put('articles/{article}', 'ArticleController@update'); Route::delete('articles/{article}', 'ArticleController@delete'); }); ~~~ 这样就不需要为每个路由设置中间件,现在看来虽然节省不了多少时间,但随着应用体量的增长,这样做的好处是保持路由的DRY(Don't Repeat Yourself)。 That's All !