Based on the laravel defaultauthImplement API authentication

Microservices are becoming more and more popular. A lot of things are broken up into separate systems that have no direct relationship to each other. So if we do user authentication must be unified to do an independent user authentication system, rather than each business system to write again user authentication related things. However, there is a problem. Laravel’s default Auth authentication is done based on the database. What about microservice architecture?

The implementation code is as follows:

UserProvider interface:

// Obtain the authentication model by unique identifier
public function retrieveById($identifier);
// Obtain the model from the unique identifier and remember token
public function retrieveByToken($identifier, $token);
// Update the remember token with the given authentication model
public function updateRememberToken(Authenticatable $user, $token);
// Obtain the user with the given credentials, such as email, username, etc
public function retrieveByCredentials(array $credentials);
// Authenticates whether a given user matches a given credential
public function validateCredentials(Authenticatable $user, array $credentials);
Copy the code

Laravel has two default user providers: DatabaseUserProvider & EloquentUserProvider. DatabaseUserProvider Illuminate\Auth\DatabaseUserProvider

The authentication model is obtained directly from the database table.

EloquentUserProvider Illuminate\Auth\EloquentUserProvider

Get the authentication model using the Eloquent model


Based on this knowledge, it is easy to customize an authentication.

The customProvider

Create a custom authentication model that implements the Authenticatable interface;

App\Auth\UserProvider.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; }}Copy the code

Authenticatable interface:

Illuminate\Contracts\Auth\Authenticatable Authenticatable defines an interface that a model or class needs to implement that can be used for authentication. That is, if a custom class is needed for authentication, the methods defined by this interface need to be implemented.


      .// Get the field name that uniquely identifies and can be used for authentication, such as ID, UUID
public function getAuthIdentifierName(a);
// Get the value corresponding to the identifier
public function getAuthIdentifier(a);
// Obtain the password for authentication
public function getAuthPassword(a);
// Obtain the remember token
public function getRememberToken(a);
// Set remember token
public function setRememberToken($value);
// Obtain the name of a remember_token field, such as the default 'remember_token'
public function getRememberTokenName(a); .Copy the code

The Authenticatable trait defined in Laravel is also the trait used by the default User model of Laravel Auth. This trait defines the default authentication identifier for the User model to be ‘ID’ and the password field to be password. Remember_token and so on. Some Settings can be changed by overriding these methods of the User model.

Implement a custom authentication model

App\Models\User.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';

    /** * get the name of the unique field that can be used for authentication, such as id, guid *@return string
     */
    public function getAuthIdentifierName(a)
    {
        return $this->primaryKey;
    }

    /** * Get the value of the primary key *@return mixed
     */
    public function getAuthIdentifier(a)
    {
        $id = $this- > {$this->getAuthIdentifierName()};
        return $id;
    }


    public function getAuthPassword(a)
    {
        return ' ';
    }

    public function getRememberToken(a)
    {
        return ' ';
    }

    public function setRememberToken($value)
    {
        return true;
    }

    public function getRememberTokenName(a)
    {
        return ' ';
    }

    protected static function getBaseUri(a)
    {
        return config('api-host.user');
    }

    public static $apiMap = [
        'getUserByToken'= > ['method'= >'GET'.'path'= >'login/user/token'].'getUserByGuId'= > ['method'= >'GET'.'path'= >'user/guid/:guid']];/** * Get user information (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;
    }


    /** * Obtain user information (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; }}Copy the code

The RestApiModel above is our company’s encapsulation of the Guzzle for API calls between systems on a PHP project. I can’t tell you the code.

Guard interface

Illuminate\Contracts\Auth\Guard

The Guard interface defines an authentication method that implements the Authenticatable model or class, as well as some commonly used interfaces.

// Determine whether the current user is logged in to publicfunctioncheck(); // Check whether the current user is a tourist (not logged in) publicfunctionguest(); // Obtain the current authenticated user publicfunctionuser(); // Obtain the id of the current authenticated user, not necessarily an ID, but a unique field name (public) defined in the previous modelfunctionid(); // Authenticate user public according to the provided messagefunction validate(array $credentials= []); // Set the current user publicfunction setUser(Authenticatable $user);
Copy the code

StatefulGuard interface

Illuminate\Contracts\Auth\StatefulGuard

The StatefulGuard interface inherits from the Guard interface and adds a further, StatefulGuard in addition to some of the basic interfaces defined in Guard.

The newly added interfaces are:

// Try to verify whether the user is valid based on the provided credentials publicfunction attempt(array $credentials= [].$remember = false); // Log in once, do not record session or cookie publicfunction once(array $credentials= []); // Log in to the user and record session and cookie public after successful authenticationfunction login(Authenticatable $user.$remember = false); // Use the user ID to log in to publicfunction loginUsingId($id.$remember = false); // Use user ID to log in, but do not record session and cookie publicfunction onceUsingId($id); // Automatically log in to public using the remember token in cookiefunctionviaRemember(); / / logout publicfunction logout(a);Copy the code

Laravel provides three default guards: RequestGuard, TokenGuard, and SessionGuard.

RequestGuard

Illuminate\Auth\RequestGuard

The RequestGuard is a very simple guard. the RequestGuard is authenticated by passing in a closure. You can add a custom RequestGuard by calling Auth::viaRequest.

SessionGuard

Illuminate\Auth\SessionGuard

SessionGuard is the default Guard for Laravel Web authentication.

TokenGuard

Illuminate\Auth\TokenGuard

The TokenGuard applies to stateless API authentication and is authenticated by token.

Implementing customizationGuard

App\Auth\UserGuard.php


      

namespace App\Auth;

use Illuminate\Http\Request;
use Illuminate\Auth\GuardHelpers;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\UserProvider;

class UserGuard implements Guard

{
    use GuardHelpers;

    protected $user = null;

    protected $request;

    protected $provider;

    /**
     * The name of the query string item from the request containing the API token.
     *
     * @var string
     */
    protected $inputKey;

    /**
     * The name of the token "column" in persistent storage.
     *
     * @var string
     */
    protected $storageKey;

    /**
     * The user we last attempted to retrieve
     * @var* /
    protected $lastAttempted;

    /**
     * UserGuard constructor.
     * @param UserProvider $provider
     * @param Request      $request
     * @return void
     */
    public function __construct(UserProvider $provider, Request $request = null)
    {
        $this->request = $request;
        $this->provider = $provider;
        $this->inputKey = 'Authorization';
        $this->storageKey = 'api_token';
    }

    /**
     * Get the currently authenticated user.
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function user(a)
    {
        if(! is_null($this->user)) {
            return $this->user;
        }

        $user = null;

        $token = $this->getTokenForRequest();

        if(!empty($token)) {
            $user = $this->provider->retrieveByCredentials(
                [$this->storageKey => $token]
            );
        }

        return $this->user = $user;
    }

    /**
     * Rules a user's credentials.
     * @param  array $credentials
     * @return bool
     */
    public function validate(array $credentials = [])
    {
        if (empty($credentials[$this->inputKey])) {
            return false;
        }

        $credentials = [$this->storageKey => $credentials[$this->inputKey]];

        $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);

        return $this->hasValidCredentials($user, $credentials);
    }

    /**
     * Determine if the user matches the credentials.
     * @param  mixed $user
     * @param  array $credentials
     * @return bool
     */
    protected function hasValidCredentials($user, $credentials)
    {
        return! is_null($user) &&$this->provider->validateCredentials($user, $credentials);
    }


    /**
     * Get the token for the current request.
     * @return string
     */
    public function getTokenForRequest(a)
    {
        $token = $this->request->header($this->inputKey);

        return $token;
    }

    /**
     * Set the current request instance.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return $this
     */
    public function setRequest(Request $request)
    {
        $this->request = $request;

        return $this; }}Copy the code

Add the following code to the boot method of AppServiceProvider: App\Providers\ authServiceprovider.php


      .// auth:api -> token provider.
Auth::provider('token'.function(a) {
	return app(UserProvider::class);
});

// auth:api -> token guard.
// @throw \Exception
Auth::extend('token'.function($app, $name, array $config) {
	if($name === 'api') {
		return app()->make(UserGuard::class, [
			'provider' => Auth::createUserProvider($config['provider']),
			'request'  => $app->request,
		]);
	}
	throw new \Exception('This guard only serves "auth:api".'); }); .Copy the code
  • Add a custom Guard to the Guards array in config\auth.php. A custom Guard consists of two parts: driver and Provider.

  • Set defaults.guard to API in config\auth. PHP.


      

return [

    /* |-------------------------------------------------------------------------- | Authentication Defaults |-------------------------------------------------------------------------- | | This option controls the default authentication "guard" and password | reset options for your application. You may change these defaults | as required, but they're a perfect start for most applications. | */

    'defaults'= > ['guard'= >'api'.'passwords'= >'users',]./* |-------------------------------------------------------------------------- | Authentication Guards |-------------------------------------------------------------------------- | | Next, you may define every authentication guard for your application. | Of course, a great default configuration has been defined for you | here which uses session storage and the Eloquent user provider.  | | All authentication drivers have a user provider. This defines how the | users are actually retrieved out of your database or other storage | mechanisms used by this application to persist your user's data. | | Supported: "session", "token" | */

    'guards'= > ['web'= > ['driver'= >'session'.'provider'= >'users',].'api'= > ['driver'= >'token'.'provider'= >'token',]],/* |-------------------------------------------------------------------------- | User Providers |-------------------------------------------------------------------------- | | All authentication drivers have a user provider. This defines how the | users are actually retrieved out of your database or other storage | mechanisms used by  this application to persist your user's data. | | If you have multiple user tables or models you may configure multiple  | sources which represent each model / table. These sources may then | be assigned to any extra authentication guards you have defined. | | Supported: "database", "eloquent" | */

    'providers'= > ['users'= > ['driver'= >'eloquent'.'model' => App\Models\User::class,
        ],

        'token'= > ['driver'= >'token'.'model' => App\Models\User::class,
        ],
    ],

    /* |-------------------------------------------------------------------------- | Resetting Passwords |-------------------------------------------------------------------------- | | You may specify multiple password reset configurations if you have more | than one user table or model in the application and you want to have | separate password reset settings based on the specific user types. | | The expire time is the number of minutes that the reset token should be | considered valid. This security feature keeps tokens short-lived so | they have less time to be guessed. You may change this as needed. | */

    'passwords'= > ['users'= > ['provider'= >'users'.'table'= >'password_resets'.'expire'= >60,]]];Copy the code

Usage:

Original address reference article: Address

The first time to write so many words of the article, write bad please forgive me!!