apeescape2.com
  • メイン
  • モバイル
  • 革新
  • ツールとチュートリアル
  • その他
バックエンド

完全なユーザー認証とアクセス制御– Laravel Passportチュートリアル、Pt。 1

Webアプリケーションを開発するときは、通常、2つの層に分割することをお勧めします。中間層APIはデータベースと対話し、Web層は通常フロントエンドSPAまたはMPAで構成されます。このように、Webアプリケーションはより疎結合になり、長期的な管理とデバッグが容易になります。

APIが作成されている場合、ステートレスAPIコンテキストで認証と状態を設定することはやや問題があるように思われるかもしれません。

この記事では、LaravelとPassportを使用してAPIに完全なユーザー認証と簡単な形式のアクセス制御を実装する方法を見ていきます。での作業経験が必要です Laravel これは入門チュートリアルではないためです。



インストールの前提条件:

  • PHP 7以降、MySQL、およびApache(3つすべてを一度にインストールしたい開発者は XAMPP 。)
  • 作曲
  • Laravel 7
  • Laravelパスポート 。 APIは通常ステートレスであり、セッションを使用しないため、通常、トークンを使用してリクエスト間の状態を維持します。 Laravelは、Passportライブラリを使用して、APIでの認証に使用できる完全なOAuth2サーバーを実装します。
  • APIをテストするためのPostman、cURL、またはInsomnia-これは個人の好み次第です
  • お好みのテキストエディタ
  • Laravelヘルパー(Laravel 6.0以降の場合)-LaravelとPassportをインストールした後、次のコマンドを実行します。
composer require laravel/helpers

上記をインストールしたら、開始する準備が整いました。 .envを編集して、データベース接続を設定してください。ファイル。

Laravel Passportチュートリアル、ステップ1:ダミーリクエスト用のコントローラーとモデルを追加する

まず、ダミーリクエスト用のコントローラーとモデルを作成します。このチュートリアルでは、このモデルはあまり役に立ちません。コントローラーが操作するデータの概念を示すためだけのものです。

モデルとコントローラーを作成する前に、移行を作成する必要があります。ターミナル内—またはcmd.exe Windowsを使用している場合は、ウィンドウ-実行:

php artisan make:migration create_articles_table --create=articles

次に、database/migrationsに移動しますフォルダを開き、xxxx_xx_xx_xxxxxx_create_articles_table.phpのような名前でファイルを開きます。

upでクラスの機能として、次のように記述します。

Schema::create('articles', function (Blueprint $table) { $table->increments('id'); $table->string('title'); $table->string('body'); $table->integer('user_id'); $table->timestamps(); });

次に、Articleを作成しますモデル。これを行うには、次を実行します。

php artisan make:model Article

次に、ArticleControllerを作成します実行することによるコントローラー:

php artisan make:controller ArticleController --resource

次に、ファイルを編集しますapp/Providers/AppServiceProvider.php IlluminateSupportFacadesSchemaをインポートします追加することによるクラス:

use IlluminateSupportFacadesSchema

…ファイルの上部にあるインポートの下部に。

次に、bootで関数、次のように記述します。

Schema::defaultStringLength(191);

これがすべて完了したら、次のコマンドを実行できます。

php artisan migrate

…上記で作成した移行を適用します。

Laravel Passportチュートリアル、ステップ2:ミドルウェアの必要な部分を作成する

ここでは、APIが機能するために必要なミドルウェアを追加します。

JSON応答

最初に必要なのはForceJsonResponseですミドルウェア。すべての応答をJSONに自動的に変換します。

これを行うには、次を実行します。

php artisan make:middleware ForceJsonResponse

そして、これはApp/Http/Middleware/ForceJsonReponse.phpにあるそのミドルウェアのハンドル機能です。

public function handle($request, Closure $next) { $request->headers->set('Accept', 'application/json'); return $next($request); }

次に、ミドルウェアをapp/Http/Kernel.phpに追加します$routeMiddleware内のファイルアレイ:

'json.response' => AppHttpMiddlewareForceJsonResponse::class,

次に、それを$middlewareにも追加します同じファイル内の配列:

AppHttpMiddlewareForceJsonResponse::class,

これにより、ForceJsonResponseが確実になりますミドルウェアはすべてのリクエストで実行されます。

CORS(クロスオリジンリソースシェアリング)

私たちの消費者を許可するために Laravel REST API 別のオリジンからアクセスするには、CORSを設定する必要があります。そのために、Corsというミドルウェアを作成します。

ターミナルまたはコマンドプロンプトで、cdプロジェクトのルートディレクトリに移動して、次のコマンドを実行します。

php artisan make:middleware Cors

次に、app/Http/Middleware/Cors.phpに次のコードを追加します。

public function handle($request, Closure $next) { return $next($request) ->header('Access-Control-Allow-Origin', '*') ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS') ->header('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, X-Token-Auth, Authorization'); }

このミドルウェアをロードするには、app/Http/Kernel.phpの$routeMiddlewareに行を追加する必要がありますアレイ:

'cors' => AppHttpMiddlewareCors::class,

また、それを$middlewareに追加する必要があります以前のミドルウェアで行ったように配列:

AppHttpMiddlewareCors::class,

その後、このルートグループをroutes/api.phpに追加します。

Route::group(['middleware' => ['cors', 'json.response']], function () { // ... });

以下に示すように、すべてのAPIルートがその関数に入ります。

Laravel Passportチュートリアル、ステップ3:APIのユーザー認証コントローラーを作成する

次に、loginを使用して認証コントローラーを作成します。およびregister機能。

まず、以下を実行します。

php artisan make:controller Auth/ApiAuthController

次に、いくつかのクラスをファイルapp/Http/Controllers/Auth/ApiAuthController.phpにインポートします。これらのクラスは、loginの作成に使用されます。およびregister機能。以下を追加してクラスをインポートします。

use AppUser; use IlluminateSupportFacadesHash; use IlluminateSupportFacadesValidator; use IlluminateSupportStr;

…コントローラーの上部に。

ここで、ユーザーにLaravel API認証を追加するために、login、logout、およびregisterを作成します。 (サインアップ)同じファイル内の関数。

register関数は次のようになります。

public function register (Request $request) { $validator = Validator::make($request->all(), [ 'name' => 'required|string|max:255', 'email' => 'required|string|email|max:255|unique:users', 'password' => 'required|string|min:6|confirmed', ]); if ($validator->fails()) { return response(['errors'=>$validator->errors()->all()], 422); } $request['password']=Hash::make($request['password']); $request['remember_token'] = Str::random(10); $user = User::create($request->toArray()); $token = $user->createToken('Laravel Password Grant Client')->accessToken; $response = ['token' => $token]; return response($response, 200); }

login関数は次のようになります:

public function login (Request $request) { $validator = Validator::make($request->all(), [ 'email' => 'required|string|email|max:255', 'password' => 'required|string|min:6|confirmed', ]); if ($validator->fails()) { return response(['errors'=>$validator->errors()->all()], 422); } $user = User::where('email', $request->email)->first(); if ($user) { if (Hash::check($request->password, $user->password)) { $token = $user->createToken('Laravel Password Grant Client')->accessToken; $response = ['token' => $token]; return response($response, 200); } else { $response = ['message' => 'Password mismatch']; return response($response, 422); } } else { $response = ['message' =>'User does not exist']; return response($response, 422); } }

そして最後に、logout関数:

public function logout (Request $request) { $token = $request->user()->token(); $token->revoke(); $response = ['message' => 'You have been successfully logged out!']; return response($response, 200); }

この後、login、register、およびlogoutを追加する必要があります私たちのルートへの機能、すなわち、 ルートグループ内 すでにAPIにあります:

Route::group(['middleware' => ['cors', 'json.response']], function () { // ... // public routes Route::post('/login', 'Auth [email protected] ')->name('login.api'); Route::post('/register','Auth [email protected] ')->name('register.api'); Route::post('/logout', 'Auth [email protected] ')->name('logout.api'); // ... });

最後に、HasApiTokenを追加する必要がありますUserへの特性モデル。 app/Userに移動しますそして、あなたが持っていることを確認してください:

use HasApiTokens, Notifiable;

…クラスのトップに。

これまでのところ…

アプリケーションサーバーを起動した場合(つまり、php artisan serveを実行した場合)、GETを送信しようとします。ルート/api/userにリクエストすると、次のメッセージが表示されます。

{ 'message': 'Unauthenticated.' }

これは、認証されていないためです そのルートにアクセスする 。選択したルートを保護するために、それらをroutes/api.phpに追加できます。 Route::postの直後行:

Route::middleware('auth:api')->group(function () { // our routes to be protected will go in here });

先に進む前に、ログアウトルートをauth:apiに追加しますLaravelはトークンを使用してユーザーをログアウトするためのミドルウェア— auth:apiの外部からアクセスできないトークンミドルウェア。私たちの 公衆 ルートは次のようになります。

Route::group(['middleware' => ['cors', 'json.response']], function () { // ... // public routes Route::post('/login', 'Auth [email protected] ')->name('login.api'); Route::post('/register', 'Auth [email protected] ')->name('register.api'); // ... });

私たちの 保護 一方、ルートは次のようになります。

Route::middleware('auth:api')->group(function () { // our routes to be protected will go in here Route::post('/logout', 'Auth [email protected] ')->name('logout.api'); });

次に、ArticleControllerに移動しますapp/Http/Controllers/ArticleController.phpで作成しましたcreateを削除しますおよびeditそのクラスのメソッド。その後、残りの各関数に、少し編集した次のコードを追加します。

$response = ['message' => ' function']; return response($response, 200);

必要に応じて記入します。たとえば、update関数はこれを本体として持ちます:

$response = ['message' => 'update function']; return response($response, 200);

手動Laravel認証テスト:ユーザーの作成

ユーザーを登録するために、POSTを送信します/api/registerへのリクエスト次のパラメーターを使用:name、email (一意である必要があります)、password、およびpassword_confirmation。

Postmanを使用して/ api / registerにPOSTリクエストを送信するスクリーンショット。

t9キーボードとは何ですか

ユーザーが作成されると、APIはトークンを返します。トークンは、認証の手段として以降のリクエストで使用されます。

ログインするには、POSTを送信します/api/loginへのリクエスト。資格情報が正しければ、この方法でLaravelログインAPIからトークンも取得します。

Postmanを使用して/ api / loginにPOSTリクエストを送信するスクリーンショット。

このリクエストから返される認証トークンは、保護されたルートにアクセスするときに使用できます。 Postmanでは、[認証]タブにドロップダウンがあり、タイプを[ベアラートークン]に設定できます。その後、トークンをトークンフィールドに入力できます。

プロセスは非常に似ています 不眠症 。

cURLユーザーは、パラメーター-H 'Authorization: Bearer 'を渡すことで同等のことを行うことができます。ここで、ログインまたは登録応答から与えられた認証トークンです。

cURLと同様に、開発者がaxiosまたはその種のライブラリを使用してAPIを使用することを計画している場合は、Authorizationを追加できます。値がBearer のヘッダー。

Laravel Passportチュートリアル、ステップ4:パスワードリセット機能の作成

基本認証が完了したので、次はパスワードリセット機能を設定します。

これを行うには、api_authを作成することを選択できますコントローラディレクトリ、新しいカスタムコントローラを作成し、関数を実装します。または、Laravelで生成できる認証コントローラーを編集することもできます。この場合、アプリケーション全体がAPIであるため、認証コントローラーを編集します。

まず、以下を実行して認証コントローラーを生成します。

composer require laravel/ui php artisan ui vue --auth

app/Http/Controllers/Auth/ForgotPasswordController.phpでクラスを編集し、次の2つのメソッドを追加します。

protected function sendResetLinkResponse(Request $request, $response) { $response = ['message' => 'Password reset email sent']; return response($response, 200); } protected function sendResetLinkFailedResponse(Request $request, $response) { $response = 'Email could not be sent to this email address'; return response($response, 500); }

次に、実際にパスワードをリセットするコントローラーを設定する必要があるため、app/Http/Controllers/Auth/ResetPasswordController.phpに移動します。次のようなデフォルトの関数をオーバーライドします。

protected function resetPassword($user, $password) { $user->password = Hash::make($password); $user->save(); event(new PasswordReset($user)); } protected function sendResetResponse(Request $request, $response) { $response = ['message' => 'Password reset successful']; return response($response, 200); } protected function sendResetFailedResponse(Request $request, $response) { $response = 'Token Invalid'; return response($response, 401); }

また、次を追加して、コントローラーにいくつかのクラスをインポートする必要があります。

use IlluminateAuthEventsPasswordReset; use IlluminateHttpRequest; use IlluminateSupportFacadesHash;

…コントローラーの上部に。

Laravelに付属するメール通知は承認にAPIトークンを使用しないため、使用するメール通知も変更する必要があります。 app/Notificationsの下に新しいものを作成できますこのコマンドを実行することにより:

php artisan make:notification MailResetPasswordNotification

ファイルを編集する必要がありますapp/Notifications/MailResetPasswordNotification.phpこのように見えるように:

pageUrl = 'localhost:8080'; // we can set whatever we want here, or use .env to set environmental variables } /** * Get the notification's delivery channels. * * @param mixed $notifiable * @return array */ public function via($notifiable) { return ['mail']; } /** * Get the mail representation of the notification. * * @param mixed $notifiable * @return IlluminateNotificationsMessagesMailMessage */ public function toMail($notifiable) { if (static::$toMailCallback) { return call_user_func(static::$toMailCallback, $notifiable, $this->token); } return (new MailMessage) ->subject(Lang::getFromJson('Reset application Password')) ->line(Lang::getFromJson('You are receiving this email because we received a password reset request for your account.')) ->action(Lang::getFromJson('Reset Password'), $this->pageUrl.'?token='.$this->token) ->line(Lang::getFromJson('This password reset link will expire in :count minutes.', ['count' => config('auth.passwords.users.expire')])) ->line(Lang::getFromJson('If you did not request a password reset, no further action is required.')); } /** * Get the array representation of the notification. * * @param mixed $notifiable * @return array */ public function toArray($notifiable) { return [ // ]; } }

この新しい通知を利用するには、sendPasswordResetNotificationをオーバーライドする必要がありますUserという方法Authenticatableから継承しますクラス。これをapp/User.phpに追加するだけです。

public function sendPasswordResetNotification($token) { $this->notify(new AppNotificationsMailResetPasswordNotification($token)); }

メールの設定が適切に機能していれば、この時点で通知が機能しているはずです。

現在残っているのは、ユーザーアクセス制御だけです。

Laravel Passportチュートリアル、ステップ5:アクセス制御ミドルウェアの作成

アクセス制御ミドルウェアを作成する前に、userを更新する必要があります。 typeという名前の列を持つテーブル。これは、ユーザーレベルを決定するために使用されます。タイプ0は通常のユーザー、タイプ1は管理者、タイプ2はスーパー管理者です。

userを更新するにはテーブルでは、これを実行して移行を作成する必要があります。

php artisan make:migration update_users_table_to_include_type --table=users

database/migrations/[timestamp]_update_users_table.phpの形式で新しく作成されたファイルで、upを更新する必要がありますおよびdown typeを追加および削除する関数それぞれ、列:

public function up() { Schema::table('users', function (Blueprint $table) { $table->integer('type'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('users', function (Blueprint $table) { $table->dropIfExists('type'); }); }

次に、php artisan migrateを実行します。これが完了したら、registerを編集する必要がありますApiAuthController.phpの関数ファイル、$user = User::create($request->toArray());の行の直前にこれを追加します。

$request['type'] = $request['type'] ? $request['type'] : 0;

また、この行を$validatorに追加する必要がありますアレイ:

'type' => 'integer',

これら2つの編集の最初のものは、デフォルトですべての登録ユーザーを「通常のユーザー」にします。つまり、ユーザータイプが入力されていない場合です。

アクセス制御ミドルウェア自体

これで、アクセス制御に使用する2つのミドルウェアを作成できるようになりました。1つは管理者用、もう1つはスーパー管理者用です。

実行します:

php artisan make:middleware AdminAuth php artisan make:middleware SuperAdminAuth

まず、app/Http/Middleware/AdminAuth.phpに移動しますIlluminateSupportFacadesAuthをインポートしてから、handleを編集しますそのように機能します:

public function handle($request, Closure $next) { if (Auth::guard('api')->check() && $request->user()->type >= 1) { return $next($request); } else { $message = ['message' => 'Permission Denied']; return response($message, 401); } }

handleも編集する必要がありますapp/Http/Middleware/SuperAdminAuth.phpの関数:

public function handle($request, Closure $next) { if (Auth::guard('api')->check() && $request->user()->type >= 2) { return $next($request); } else { $message = ['message' => 'Permission Denied']; return response($message, 401); } }

Authもインポートする必要があります以下を追加して、両方のファイルの先頭にクラスを追加します。

use IlluminateSupportFacadesAuth;

…そこにある輸入品の一番下まで。

新しいミドルウェアを使用するために、app/Http/Kernel.phpに次の行を追加して、カーネル内、つまり$routeMiddleware内の両方のクラスを参照します。アレイ:

'api.admin' => AppHttpMiddlewareAdminAuth::class, 'api.superAdmin' => AppHttpMiddlewareSuperAdminAuth::class,

開発者が特定のルートでミドルウェアを使用したい場合は、次のようにミドルウェアをルート関数に追加するだけです。

Route::post('route',' [email protected] ')->middleware('');

この場合、必要に応じてapi.admin、api.superAdminなどになります。

ミドルウェアを作成するために必要なのはこれだけです。

すべてを一緒に入れて

認証とアクセス制御が機能していることをテストするために、いくつかの追加手順を実行する必要があります。

Laravel認証とアクセス制御のテスト:ステップ1

ArticleControllerのindexを変更する必要があります機能し、ルートを登録します。 (実際のプロジェクトでは、PHPUnitを使用し、自動テストの一部としてこれを実行します。ここでは、テスト目的でルートを手動で追加しています。後で削除できます。)

ArticleControllerに移動しますapp/Http/Controllers/ArticleControllerのコントローラーindexを変更しますこのように見える関数:

public function index() { $response = ['message' => 'article index']; return response($response, 200); }

次に、routes/api.phpに移動して、ルートに関数を登録します。ファイルとこれを追加します:

Route::middleware('auth:api')->group(function () { Route::get('/articles', ' [email protected] ')->name('articles'); });

Laravel認証とアクセス制御のテスト:ステップ2

これで、認証トークンなしでルートへのアクセスを試みることができます。認証エラーが発生するはずです。

Postmanを使用して/ api / articlesにGETリクエストを送信し、メッセージを受信するスクリーンショット

Laravel認証とアクセス制御のテスト:ステップ3

また、認証トークン(この記事の前半で登録またはログインして取得したもの)を使用して同じルートにアクセスすることもできます。

これにより、次のようなエラーが発生する場合があります。

Unknown column 'api_token' in 'where clause' (SQL: select * from `users` where `api_token` = ...

Postmanを使用してapi / articlesルートを取得したときの不明な列エラーのスクリーンショット。

これが発生した場合、開発者は 確認してください Passport移行を実行し、['guards']['api']['driver']を持っているpassportに設定config/auth.phpで:

'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'passport', 'provider' => 'users', ], ],

その後、構成キャッシュも更新する必要があります。

それが修正されると、ルートにアクセスできるようになります。

通常のJSON応答を使用して、Postmanを使用して/ api / articlesにGETリクエストを送信するスクリーンショット。

Laravel認証とアクセス制御のテスト:ステップ4

アクセス制御をテストする時が来ました。追加しましょう->middleware('api.admin')記事のルートに移動するため、次のようになります。

Route::get('/articles', ' [email protected] ')->middleware('api.admin')->name('articles');

api/userでわかるように、新しく作成されたユーザーには自動的にタイプ0が割り当てられるようにしました。ルート。

Postmanを使用して/ api / userにGETリクエストを送信するスクリーンショット。応答には、ID、名前、電子メール、null電子メール検証タイムスタンプ、入力された作成および更新されたタイムスタンプ、およびタイプが含まれます。

そのため、articlesにアクセスしようとするとエラーが発生するはずです。そのようなユーザーとしてのエンドポイント。

Postmanを使用して/ api / articlesにGETリクエストを送信し、JSON応答が許可を拒否したスクリーンショット。

テストの目的で、データベース内のユーザーを変更してtypeを設定しましょう。 api/userを介してその変更を確認した後もう一度ルートを設定して、GETに再試行する準備ができました/articles/ルート。

Postmanを使用してLaravel認証ユーザーとして/ api / articlesにGETリクエストを送信するスクリーンショット。これは、以前のリクエストと同じですが、これが例外です。

それは完璧に動作します。

より複雑なアプリケーションを作成している開発者は、適切なアクセス制御がこれほど単純ではないことに注意する必要があります。その場合、他のサードパーティアプリケーションまたはLaravelの ゲートとポリシー カスタムユーザーアクセス制御を実装するために使用できます。このシリーズの第2部では、より堅牢で柔軟なアクセス制御ソリューションについて説明します。

すべてのデバイスのブートストラップメディアクエリ

Laravel API認証:私たちが学んだこと

このLaravelPassportチュートリアルでは、以下について説明しました。

  1. LaravelPassportの例をテストするときに使用するものを持つダミーのコントローラーとモデルを作成します。
  2. APIをスムーズに実行するために必要なミドルウェアを作成し、CORSに対処し、APIに常にJSON応答を返すように強制します。
  3. 基本的なLaravelAPI認証の設定:登録、ログイン、およびログアウト。
  4. Laravelのデフォルトに基づいて「パスワードリセット」機能を設定します。
  5. アクセス制御ミドルウェアを作成して、さまざまなルートにユーザー認証アクセス許可レベルを追加します。

これらは、の分野で働く人にとって不可欠なスキルです Laravel開発サービス 。読者は最終結果を このGitHubリポジトリ これで、Laravelで認証を実装できるようになりました。以下のコメントをお待ちしております。

基本を理解する

Laravelパスポートとは何ですか?

Laravel Passportは、Laravelを使用したAPI認証用のOAuth2.0サーバー実装です。トークンは一般的にAPI認証で使用されるため、Laravel Passportは、OAuth2.0サーバーにトークン認証を実装するための簡単で安全な方法を提供します。

Laravelパスポートの用途は何ですか?

Laravel Passportは、Laravel RESTAPIで認証を実装するために使用されるパッケージです。

Laravelパスポートは安全ですか?

Laravel Passportは、ステートレス認証用のOAuth2.0サーバー実装です。 OAuth 2.0は最新のOAuthプロトコルであり、安全です。

ステートレスAPIとは何ですか?

ステートレスAPIは、それに対する各リクエストが完全に分離されており、各リクエストの応答がリクエストのみに完全に依存しているAPIです。これは、各リクエストの応答がサーバーとリクエストの「状態」に依存するステートフルアプリケーションとは異なります。

仕事をしながら旅行する方法:旅行エンジニアのサバイバルガイド

ライフスタイル

仕事をしながら旅行する方法:旅行エンジニアのサバイバルガイド
MicrosoftHoloLensレビュー-ARとVRの間のギャップを埋める

MicrosoftHoloLensレビュー-ARとVRの間のギャップを埋める

モバイル

人気の投稿
ソフトウェアリエンジニアリング:スパゲッティからクリーンデザインまで
ソフトウェアリエンジニアリング:スパゲッティからクリーンデザインまで
効率的なアプローチ-無駄のないUXMVPを設計する方法
効率的なアプローチ-無駄のないUXMVPを設計する方法
モバイルユーザビリティの基本ガイド
モバイルユーザビリティの基本ガイド
DevOps:それが何であり、なぜそれが重要なのか
DevOps:それが何であり、なぜそれが重要なのか
ワイヤーフレームの死。ハイフィデリティに直行しましょう!
ワイヤーフレームの死。ハイフィデリティに直行しましょう!
 
私のCakePHP3レビュー–まだ新鮮で、まだ暑い
私のCakePHP3レビュー–まだ新鮮で、まだ暑い
開発スケッチプラグインに精通する
開発スケッチプラグインに精通する
単一責任の原則:優れたコードのレシピ
単一責任の原則:優れたコードのレシピ
在宅勤務ライフスタイルのトレンド収集
在宅勤務ライフスタイルのトレンド収集
宣言型プログラミング:それは本物ですか?
宣言型プログラミング:それは本物ですか?
人気の投稿
  • anglejsでディレクティブを使用する方法
  • ブートストラップを使用してWebサイトを構築する
  • ノードjsとJavaのパフォーマンス
  • メディアクエリの書き方
  • 企業の資本予算承認プロセスは、いくつのステップで構成されていますか?
カテゴリー
Webフロントエンド バックエンド Uiデザイン 収益性と効率性 ツールとチュートリアル デザイナーライフ 財務プロセス エンジニアリング管理 設計プロセス 製品の担当者とチーム

© 2021 | 全著作権所有

apeescape2.com