/ it-stuff

ajax验证调用laravel传统auth api的解决方案记录

Laravel不太熟悉,感觉官方文档不咋地,有些地方一笔带过很粗略,没实际玩过很难理解,可能系统本身复杂,写得太详细就会繁冗拖沓没人看了吧。

实现网页注册登录和api验证是网站的最基本功能,Laravel提供了不止两种解决方案,这篇文章要说的是一比较老的传统方案,比较新的方案应该是JWT(Json Web Token)+angular或 Passport+Vue网上文档博客各种支持很多,这里就没什么好赘述了。这篇文章是为了帮助使用web form login + jquery ajax api在laravel实现找不到方向的小伙伴。

所谓web form login + jquery ajax api方案,就是用laravel的php artisan make:auth自带一套注册登录验证系统,使用html web form注册登录,验证过后通过session或cookie来记录已登录状态,然后前端使用jquery ajax调用服务端api;那么问题来了,服务端如何来验证调用方的有效身份呢。在Google上一顿狂搜后居然没有找到这么个基本需求的完整解决方案,倒是找到了不少JWT的资料,问题是如果使用JWT,似乎要把前端web form全部改成用jquery ajax的,因为web form一提交就把网页重定向到不知道哪里去了(服务端所控制),没有机会在前端处理返回回来的token啊。回过头来再仔细找基于laravel auth框架的api验证实现,终于找到下面这个网页:https://laracasts.com/discuss/channels/laravel/53-api-routes-auth-middleware-confusion
这哥们自己解答了自己提出的问题。还给了一个链接到另外一篇博客:
https://gistlog.co/JacobBennett/090369fbab0b31130b51

  • If you've setup laravel's default auth system, you will also need to add a column for api_token to the user table. If you are using DB seeders, you might want to add something like: $table->char('api_token', 60)->nullable(); to your users table seeder. Alternatively just add the column manually and fill that column with a random 60-char key.
  • When making the request, you can add the api_token as a URL/Querystring parameter like so: domain.com/api/test?api_token=[your 60 char key]. You can also send the key as a header (if using Postman or similar), i.e: Header: Authorization, Value: Bearer [your 60 char key].
  • I order to get a useful error if the token is incorrect, also send the following header with all requests: Header: Accept, Value: application/json. This allows the expectsJson() check in the unauthenticated() function inside App/Exceptions/Handler.php to work correctly.

按照他说的步骤一一做了,的确接近正解了;但是,还有不少坑需要填。。。
例如,这个api_token如何发给客户端没交代啊,自己实现吧,重载LoginControllersendLoginResponse方法,利用withCookie把api_token放在cookie中返回给前端。

$apiuser = $this->guard()->user();

return $this->authenticated($request, $this->guard()->user())
                ?: redirect()->intended($this->redirectPath())->withCookie(
                    Cookie::make('api_token', $apiuser->api_token, 24 * 60, null, null, false, false)
                );

这里的一个坑是创建Cookie时要指定httponly参数为false,也就是Cookie::make的最后一个参数,不然的话客户端javascript取不到api_token这个Cookie。

然后在jquery ajax里如那个"meredevelopment"哥们所说指定设置:

headers: {
  "Accept": "application/json",
  "Authorization": "Bearer " + Cookies.get("api_token")
}

顺便说一下,这里的Cookie.get不是天然就有的,而是来自于js-cookie库

接下来在Laravel的api.php里,把需要验证的api route都用auth:api中间件‘套’起来,从而实现api 验证

Route::group(['middleware' => ['auth:api']], function () {
    ...
    Route::get('/users/query', 'UserController@query');
    ...
});

走到这里,还有(最后一个)坑需要填。Laravel默认给Cookie加密,如果api_token加密了,auth:api中间件似乎就不能工作了。找到EncryptCookies.php,把api_token加入$except数组即可。