Шлюз в PHP

RedDeveloper
25.01.2023 10:51
Шлюз в PHP

Что делает API-шлюз?

API-шлюз (AG) - это сервер, который действует как единая точка входа для набора микросервисов.

API Gateway получает клиентские запросы, направляет их в соответствующий микросервис, а затем возвращает ответ сервера клиенту.

Шлюз отвечает за такие задачи, как маршрутизация, аутентификация и ограничение скорости. Это позволяет микросервисам сосредоточиться на своих индивидуальных задачах и повышает общую производительность и масштабируемость системы.

Тогда мы можем сказать в двух словах: "API-шлюз - это инструмент управления API, который находится между клиентом и набором бэкенд-сервисов".

Зачем использовать API-шлюз?

Тогда мы можем сказать в двух словах "API-шлюз - это инструмент управления API которыйЭто изображение использовано из поиска google

Большинство корпоративных API развертываются через API-шлюзы. Обычно API-шлюзы выполняют общие задачи, которые используются в системе API-сервисов, такие как аутентификация пользователей, ограничение скорости и статистика.

В самом простом виде API-сервис принимает удаленный запрос и возвращает ответ. Но в реальной жизни все не так просто. Рассмотрим различные проблемы, возникающие при размещении крупномасштабных API.

  • Вы хотите защитить API от чрезмерного использования и злоупотреблений, поэтому вы используете службу аутентификации и ограничение скорости.
  • хотите понять, как люди используют ваши API, поэтому вы добавили инструменты аналитики и мониторинга.
  • Если у вас есть монетизированные API, вы захотите подключиться к биллинговой системе.
  • Возможно, вы перешли на архитектуру микросервисов, в этом случае один запрос может потребовать обращения к десяткам отдельных приложений.
  • Со временем вы добавите несколько новых API-сервисов и уберете другие, но ваши клиенты все равно захотят найти все ваши сервисы в одном месте.

Сложность заключается в том, чтобы предложить вашим клиентам простой и надежный опыт работы в условиях всей этой сложности. API-шлюз - это способ отделить клиентский интерфейс от реализации бэкенда. Когда клиент делает запрос, API-шлюз разбивает его на несколько запросов, направляет их в нужные места, выдает ответ и все отслеживает.

является самым популярным API-шлюзом:

𝗥𝗼𝘂𝘁𝗶𝗻𝗴: API-шлюз получает запросы от клиентов и направляет их в соответствующий микросервис. Это позволяет клиентам получать доступ к различным микросервисам через единую точку входа, упрощая общий дизайн системы.

𝗦𝗲𝗰𝘂𝗿𝗶𝘁𝘆 API-шлюз может использоваться для аутентификации клиентов и применения политик контроля доступа к микросервисам. Это помогает обеспечить доступ к микросервисам только авторизованным клиентам и предотвратить несанкционированный доступ.

𝐓𝐫𝐚𝐧𝐬𝐟𝐨𝐫𝐦𝗶𝗻𝗴 𝐫𝐞𝐪𝐮𝐞𝐬𝐭𝐬 𝐚𝐧𝐝 𝐫𝐞𝐬𝐩𝐨𝐧𝐬𝐞𝐬: API Gateway может преобразовывать входящие запросы и исходящие ответы на бэкенд и из бэкенда для удовлетворения потребностей различных клиентов или соответствия различным архитектурам бэкенда.

𝗥𝗮𝘁𝗲𝗹𝗶𝗺𝗶𝘁𝗶𝗻𝗴: Вы можете ограничить доступ клиентов к микросервисам с помощью шлюза API. Это поможет предотвратить атаки типа "отказ в обслуживании" и другие виды вредоносного поведения.

𝗟𝗼𝗮𝗱𝗯𝗮𝗹𝗮𝗻𝗰𝗶𝗻𝗴: API Gateway может распределять входящие запросы между несколькими экземплярами микросервиса, позволяя системе обрабатывать большее количество запросов и повышая общую производительность и масштабируемость.

𝗖𝗮𝗰𝗵𝗶𝗻𝗴: Шлюз API может кэшировать ответы микросервисов, уменьшая количество запросов, которые необходимо перенаправлять микросервисам, и повышая общую производительность системы.

𝗦𝗲𝗿𝘃𝗲𝗿𝗹𝗲𝘀𝘀𝗲𝘅𝗲𝗰𝘂𝘁𝗶𝗼𝗻: API Gateway может интегрироваться с другими сервисами, такими как AWS Lambda, для создания бессерверных архитектур и обеспечения сложной обработки запросов без необходимости использования выделенного сервера.

𝗖𝗶𝗿𝗰𝘂𝗶𝘁 𝗯𝗿𝗲𝗮𝗸𝗲𝗿: API Gateway можно использовать для реализации схем автоматического выключателя, что поможет защитить от каскадных сбоев и повысить отказоустойчивость вашей системы.

𝗥𝗲𝘃𝗲𝗿𝘀𝗲𝗽𝗿𝗼𝘅𝘆: API Gateway может действовать как обратный прокси-сервер, направляя входящие запросы в соответствующую внутреннюю службу на основе пути запроса или других критериев.

𝗔𝗣𝗜 𝘃𝗲𝗿𝘀𝗶𝗼𝗻𝗶𝗻𝗴: API Gateway можно использовать для реализации версионности API, что позволяет поддерживать несколько версий API и управлять переходом от одной версии к другой.

Реализация

Попытаюсь объяснить на базовом примере для API.

Допустим, у вас есть 3 микросервиса:

  1. Пользователи
  2. Посты
  3. Ядро

Я предполагаю, что вы используете http Only cookie для хранения токена пользователя.

В микросервисе Core у меня такая структура маршрута:

Route::prefix('core')->group(function () {
    Route::post('register', [AuthController::class, 'register']);
    Route::post('login', [AuthController::class, 'login']);

    Route::middleware('scope.trader')->group(function () {
        Route::get('user', [AuthController::class, 'user']);

    });
});

Я хочу войти в систему, для чего мне нужно отправить запрос API, и я должен придумать решение для отправки токена в любое время, когда он мне понадобится.

  1. login (именно здесь вы получаете токен) и register не нуждаются в токене
  2. пользователю нужен токен (вот где вы просили решение)

Итак, в дополнение к получению результата, я должен создать сервис для пользователя, и вот как я это сделал:

UserService :
class UserService extends ApiService
{
    public function __construct()
    {
        // Get User Endpoint Microservice API URL
        $this->endpoint = env('USERS_MS') . '/api';
    }
}
ApiService :
abstract class ApiService
{
    protected string $endpoint;

    public function request($method, $path, $data = [])
    {
        $response = $this->getRequest($method, $path, $data);

        if ($response->ok()) {return $response->json();};

        throw new HttpException($response->status(), $response->body());
    }

    public function getRequest($method, $path, $data = [])
    {
        return \Http::acceptJson()->withHeaders([
            'Authorization' =>  'Bearer ' . request()->cookie('token')
        ])->$method("{$this->endpoint}/{$path}", $data);
    }

    public function post($path, $data)
    {
        return $this->request('post', $path, $data);
    }

    public function get($path)
    {
        return $this->request('get', $path);
    }

    public function put($path, $data)
    {
        return $this->request('put', $path, $data);
    }

    public function delete($path)
    {
        return $this->request('delete', $path);
    }
}

'попытаюсь объяснить на базовом примере для API.

Допустим, у вас есть 3 микросервиса:

  1. Пользователи
  2. Посты
  3. Ядро

Я предполагаю, что вы используете httpOnly cookie для хранения токена пользователя.

В микросервисе Core у меня такая структура маршрута:

Route::prefix('core')->group(function () {
    Route::post('register', [AuthController::class, 'register']);
    Route::post('login', [AuthController::class, 'login']);
    Route::middleware('scope.trader')->group(function () {
        Route::get('user', [AuthController::class, 'user']);
    });
});

Я хочу войти в систему, для чего мне нужно отправить запрос API, и я должен придумать решение для отправки токена в любое время, когда он мне понадобится.

  1. login (именно здесь вы получаете токен) и register не нуждаются в токене
  2. пользователю нужен токен (вот где вы просили решение)

Поэтому, чтобы получить результат, я должен создать сервис для пользователя, и вот как я это сделал:

UserService :
class UserService extends ApiService
{
    public function __construct()
    {
        // Get User Endpoint Microservice API URL
        $this->endpoint = env('USERS_MS') . '/api';
    }
}
ApiService :
abstract class ApiService
{
    protected string $endpoint;
    public function request($method, $path, $data = [])
    {
        $response = $this->getRequest($method, $path, $data);
        if ($response->ok()) {return $response->json();};
        throw new HttpException($response->status(), $response->body());
    }
    public function getRequest($method, $path, $data = [])
    {
        return \Http::acceptJson()->withHeaders([
            'Authorization' =>  'Bearer ' . request()->cookie('token')
        ])->$method("{$this->endpoint}/{$path}", $data);
    }
    public function post($path, $data)
    {
        return $this->request('post', $path, $data);
    }
    public function get($path)
    {
        return $this->request('get', $path);
    }
    public function put($path, $data)
    {
        return $this->request('put', $path, $data);
    }
    public function delete($path)
    {
        return $this->request('delete', $path);
    }
}

Если вам интересно, откуда взялся этот UserService, то я должен сказать, что я создал пакет для использования его в других микросервисах, так что вы можете сделать то же самое или просто создать сервис и использовать его в своих микросервисах или т.д.

Про ApiService все очевидно, но я постараюсь объяснить основу.

  1. мы хотим сделать вызов API, мы можем просто вызвать методы Allowed в этом классе, затем наши методы будут вызывать запросы, чтобы передать общие аргументы, и в конечном итоге использовать эти аргументы для выполнения вызова API.
  2. метод выполняет вызов и получает сохраненный токен из куки httpOnly, и отправляет его в качестве заголовка Authorization конечной точке, и в итоге возвращает все, что получил от цели.

Итак, если мы хотим использовать это, мы можем просто сделать вот так в нашем контроллере:

class AuthController extends Controller
{
    // use Services\UserService;
    public UserService $userService;

    /**
     * @param UserService $userService
     */
    public function __construct(UserService $userService)
    {
        $this->userService = $userService;
    }

    public function register(RegisterRequest $request)
    {
        $data = $request->only('name', 'email', 'password') + ['additional_fileds' => 0 ];
        // additional fields can be used for something except from request and
        // optional, like is it admin or user or etc.

        // call the post method, pass the endpoint url(`register`), pass $data
        $user = $this->userService->post('register', $data);
        // get data from target endpoint
        // and ...
        return response($user, Response::HTTP_CREATED);
    }

    public function login(Request $request)
    {
        // same thing here again, but this time i passed scope to help me
        // get the specific user scope
        $data = $request->only('email', 'password') + ['scope' => 'writer'];

        $response = $this->userService->post('login', $data);
        // as you can see when user do success login, we will get token,
        // which i got that token using Passport and set it to $cookie
        $cookie = cookie('token', $response['token'], 60 * 24); // 1 day
      
        // then will set a new httpOnly token on response.
        return response([
            'message' => 'success'
        ])->withCookie($cookie);
    }

    public function user(Request $request)
    {
        // Here, base on userService as you saw, we passed token in all requests
        // which if token exist, we get the result, since we're expecting
        // token to send back the user informations.

        $user = $this->userService->get('user');

        // get posts belong to authenticated user
        $posts = Post::where('user_id', $user['id'])->get();

        $user['posts'] = $posts;

        return $user;
    }
}

'попытаюсь объяснить на базовом примере для API.

Допустим, у вас есть 3 микросервиса:

  1. Пользователи
  2. Посты
  3. Ядро

Я предполагаю, что вы используете httpOnly cookie для хранения токена пользователя.

В микросервисе Core у меня такая структура маршрута:

Route::prefix('core')->group(function () {
    Route::post('register', [AuthController::class, 'register']);
    Route::post('login', [AuthController::class, 'login']);
    Route::middleware('scope.trader')->group(function () {
        Route::get('user', [AuthController::class, 'user']);
    });
});

Я хочу войти в систему, для чего мне нужно отправить запрос API, и я должен придумать решение для отправки токена в любое время, когда он мне понадобится.

  1. login (здесь вы получаете токен) и register не нуждаются в токене
  2. пользователю нужен токен (именно здесь вы просили решение)

Итак, чтобы получить результат, я должен создать сервис для пользователя, и вот как я это сделал:

UserService :
class UserService extends ApiService
{
    public function __construct()
    {
        // Get User Endpoint Microservice API URL
        $this->endpoint = env('USERS_MS') . '/api';
    }
}
ApiService :
abstract class ApiService
{
    protected string $endpoint;
    public function request($method, $path, $data = [])
    {
        $response = $this->getRequest($method, $path, $data);
        if ($response->ok()) {return $response->json();};
        throw new HttpException($response->status(), $response->body());
    }
    public function getRequest($method, $path, $data = [])
    {
        return \Http::acceptJson()->withHeaders([
            'Authorization' =>  'Bearer ' . request()->cookie('token')
        ])->$method("{$this->endpoint}/{$path}", $data);
    }
    public function post($path, $data)
    {
        return $this->request('post', $path, $data);
    }
    public function get($path)
    {
        return $this->request('get', $path);
    }
    public function put($path, $data)
    {
        return $this->request('put', $path, $data);
    }
    public function delete($path)
    {
        return $this->request('delete', $path);
    }
}

Вы задаетесь вопросом, откуда взялся этот UserService, то я должен сказать, что я создал пакет для использования его в других микросервисах, так что вы можете сделать то же самое или просто создать сервис и использовать его в своих микросервисах или т.д.

Про ApiService все очевидно, но я постараюсь объяснить основу.

  1. мы хотим сделать вызов API, мы можем просто вызвать методы Allowed в этом классе, затем наши методы вызовут request, чтобы передать общие аргументы, и, в конечном итоге, используя эти аргументы, сделать вызов API.
  2. метод выполняет вызов и получает сохраненный токен из куки httpOnly, и отправляет его в качестве заголовка Authorization в целевую конечную точку, и в итоге возвращает все, что получил от цели.

Итак, если мы хотим использовать это, мы можем просто сделать вот так в нашем контроллере:

class AuthController extends Controller
{
    // use Services\UserService;
    public UserService $userService;
    /**
     * @param UserService $userService
     */
    public function __construct(UserService $userService)
    {
        $this->userService = $userService;
    }
    public function register(RegisterRequest $request)
    {
        $data = $request->only('name', 'email', 'password') + ['additional_fileds' => 0 ];
        // additional fields can be used for something except from request and
        // optional, like is it admin or user or etc.
        // call the post method, pass the endpoint url(`register`), pass $data
        $user = $this->userService->post('register', $data);
        // get data from target endpoint
        // and ...
        return response($user, Response::HTTP_CREATED);
    }
    public function login(Request $request)
    {
        // same thing here again, but this time i passed scope to help me
        // get the specific user scope
        $data = $request->only('email', 'password') + ['scope' => 'writer'];
        $response = $this->userService->post('login', $data);
        // as you can see when user do success login, we will get token,
        // which i got that token using Passport and set it to $cookie
        $cookie = cookie('token', $response['token'], 60 * 24); // 1 day
      
        // then will set a new httpOnly token on response.
        return response([
            'message' => 'success'
        ])->withCookie($cookie);
    }
    public function user(Request $request)
    {
        // Here, base on userService as you saw, we passed token in all requests
        // which if token exist, we get the result, since we're expecting
        // token to send back the user informations.
        $user = $this->userService->get('user');
        // get posts belong to authenticated user
        $posts = Post::where('user_id', $user['id'])->get();
        $user['posts'] = $posts;
        return $user;
    }
}

Как насчет пользовательского микросервиса? Ну, здесь все ясно, и он должен работать как базовое приложение.

Вот маршруты:

Route::post('register', [AuthController::class, 'register']);
Route::post('login', [AuthController::class, 'login']);

Route::middleware(['bunch','of', 'middlewares'])->group( function (){
    Route::get('user', [AuthController::class, 'user']);
});

А в контроллере :

class AuthController extends Controller
{
    public function register(Request $request)
    {
        $user = User::create(
            $request->only('first_name', 'email', 'additional_field')
            + ['password' => \Hash::make($request->input('password'))]
        );

        return response($user, Response::HTTP_CREATED);
    }


    public function login(Request $request)
    {
        if (!\Auth::attempt($request->only('email', 'password'))) {
            return response([
                'error' => 'user or pass is wrong or whatever.'
            ], Response::HTTP_UNAUTHORIZED);
        }

        $user = \Auth::user();

        $jwt = $user->createToken('token', [$request->input('here you can pass the required scope like trader as i expalined in top')])->plainTextToken;

        return compact('token');
    }

    public function user(Request $request)
    {
        return $request->user();
    }
}

Итак, вот полный пример, и вы можете использовать подход микросервиса Core на других микросервисах для получения информации, связанной с аутентифицированным пользователем, и, как вы видите, все будет аутентифицировано благодаря этим запросам от ядра к другим микросервисам.

Мой список статей в Publis

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?

20.08.2023 18:21

Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в 2023-2024 годах? Или это полная лажа?".

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией

20.08.2023 17:46

В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.

Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox

19.08.2023 18:39

Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в частности, магию поплавков и гибкость flexbox.

Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest

19.08.2023 17:22

В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для чтения благодаря своей простоте. Кроме того, мы всегда хотим проверить самые последние возможности в наших проектах!

Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️

18.08.2023 20:33

Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий их языку и культуре.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL

14.08.2023 14:49

Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип предназначен для представления неделимого значения.