基于 laravel 默认的 auth 实现 api 认证
现在微服务越来越流行了. 很多东西都拆分成独立的系统,各个系统之间没有直接的关系. 这样我们如果做用户认证肯定是统一的做一个独立的 用户认证 系统,而不是每个业务系统都要重新去写一遍用户认证相关的东西. 但是又遇到一个问题了. laravel 默认的auth 认证 是基于数据库做的,如果要微服务架构可怎么做呢?
实现代码如下:
UserProvider 接口:
// 通过唯一标示符获取认证模型 public function retrieveById($identifier); // 通过唯一标示符和 remember token 获取模型 public function retrieveByToken($identifier, $token); // 通过给定的认证模型更新 remember token public function updateRememberToken(Authenticatable $user, $token); // 通过给定的凭证获取用户,比如 email 或用户名等等 public function retrieveByCredentials(array $credentials); // 认证给定的用户和给定的凭证是否符合 public function validateCredentials(Authenticatable $user, array $credentials);
Laravel 中默认有两个 user provider : DatabaseUserProvider & EloquentUserProvider.
DatabaseUserProvider
Illuminate\Auth\DatabaseUserProvider
直接通过数据库表来获取认证模型.
EloquentUserProvider
Illuminate\Auth\EloquentUserProvider
通过 eloquent 模型来获取认证模型
根据上面的知识,可以知道要自定义一个认证很简单。
自定义 Provider
创建一个自定义的认证模型,实现 Authenticatable 接口;
App\Auth\UserProvider.php
<?php namespace App\Auth; use App\Models\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Contracts\Auth\UserProvider as Provider; class UserProvider implements Provider { /** * Retrieve a user by their unique identifier. * @param mixed $identifier * @return \Illuminate\Contracts\Auth\Authenticatable|null */ public function retrieveById($identifier) { return app(User::class)::getUserByGuId($identifier); } /** * Retrieve a user by their unique identifier and "remember me" token. * @param mixed $identifier * @param string $token * @return \Illuminate\Contracts\Auth\Authenticatable|null */ public function retrieveByToken($identifier, $token) { return null; } /** * Update the "remember me" token for the given user in storage. * @param \Illuminate\Contracts\Auth\Authenticatable $user * @param string $token * @return bool */ public function updateRememberToken(Authenticatable $user, $token) { return true; } /** * Retrieve a user by the given credentials. * @param array $credentials * @return \Illuminate\Contracts\Auth\Authenticatable|null */ public function retrieveByCredentials(array $credentials) { if ( !isset($credentials['api_token'])) { return null; } return app(User::class)::getUserByToken($credentials['api_token']); } /** * Rules a user against the given credentials. * @param \Illuminate\Contracts\Auth\Authenticatable $user * @param array $credentials * @return bool */ public function validateCredentials(Authenticatable $user, array $credentials) { if ( !isset($credentials['api_token'])) { return false; } return true; } }
Authenticatable 接口:
Illuminate\Contracts\Auth\Authenticatable
Authenticatable 定义了一个可以被用来认证的模型或类需要实现的接口,也就是说,如果需要用一个自定义的类来做认证,需要实现这个接口定义的方法。
<?php . . . // 获取唯一标识的,可以用来认证的字段名,比如 id,uuid public function getAuthIdentifierName(); // 获取该标示符对应的值 public function getAuthIdentifier(); // 获取认证的密码 public function getAuthPassword(); // 获取remember token public function getRememberToken(); // 设置 remember token public function setRememberToken($value); // 获取 remember token 对应的字段名,比如默认的 'remember_token' public function getRememberTokenName(); . . .
Laravel 中定义的 Authenticatable trait,也是 Laravel auth 默认的 User 模型使用的 trait,这个 trait 定义了 User 模型默认认证标示符为 'id',密码字段为password,remember token 对应的字段为 remember_token 等等。
通过重写 User 模型的这些方法可以修改一些设置。
实现自定义认证模型
App\Models\User.php
<?php namespace App\Models; use App\Exceptions\RestApiException; use App\Models\Abstracts\RestApiModel; use Illuminate\Contracts\Auth\Authenticatable; class User extends RestApiModel implements Authenticatable { protected $primaryKey = 'guid'; public $incrementing = false; protected $keyType = 'string'; /** * 获取唯一标识的,可以用来认证的字段名,比如 id,guid * @return string */ public function getAuthIdentifierName() { return $this->primaryKey; } /** * 获取主键的值 * @return mixed */ public function getAuthIdentifier() { $id = $this->{$this->getAuthIdentifierName()}; return $id; } public function getAuthPassword() { return ''; } public function getRememberToken() { return ''; } public function setRememberToken($value) { return true; } public function getRememberTokenName() { return ''; } protected static function getBaseUri() { return config('api-host.user'); } public static $apiMap = [ 'getUserByToken' => ['method' => 'GET', 'path' => 'login/user/token'], 'getUserByGuId' => ['method' => 'GET', 'path' => 'user/guid/:guid'], ]; /** * 获取用户信息 (by guid) * @param string $guid * @return User|null */ public static function getUserByGuId(string $guid) { try { $response = self::getItem('getUserByGuId', [ ':guid' => $guid ]); } catch (RestApiException $e) { return null; } return $response; } /** * 获取用户信息 (by token) * @param string $token * @return User|null */ public static function getUserByToken(string $token) { try { $response = self::getItem('getUserByToken', [ 'Authorization' => $token ]); } catch (RestApiException $e) { return null; } return $response; } }
上面 RestApiModel 是我们公司对 Guzzle 的封装,用于 php 项目各个系统之间 api 调用. 代码就不方便透漏了.
Guard 接口
Illuminate\Contracts\Auth\Guard
Guard 接口定义了某个实现了 Authenticatable (可认证的) 模型或类的认证方法以及一些常用的接口。