laravel 相关

  • 安装 laravel 框架,版本根据自己的实际情况选择

    composer create-project --prefer-dist laravel/laravel laravel "8.5.*"
  • .env文件中配置数据库连接

    DB_CONNECTION=mysql
    DB_HOST=127.0.0.1
    DB_PORT=3306
    DB_DATABASE=laravel
    DB_USERNAME=root
    DB_PASSWORD=De5RJZWSjJF42FkC
  • 数据库迁移

    # 在项目根目录执行
    php artisan migrate
  • 数据填充

    • 填充前准备
      DatabaseSeeder.png
    • 执行填充

      php artisan db:seed

Dingo Api 相关

  • 安装 Dingo Api

    composer require "dingo/api"
  • 在 config 目录生成配置文件api.php

    php artisan vendor:publish --provider="Dingo\Api\Provider\LaravelServiceProvider"
  • .env文件里配置Dingo Api , Dingo API 配置项说明

    # dingo api
    API_STANDARDS_TREE=x            // 环境
    API_SUBTYPE=myapp               // 子类型
    API_PREFIX=api                  // 前缀
    API_DOMAIN=api.myapp.com        // 域名
    API_VERSION=v1                  // 版本号
    API_NAME="My API"               // 名字
    API_CONDITIONAL_REQUEST=false   // 条件请求
    API_STRICT=false                // 严格模式
    API_DEFAULT_FORMAT=json         // 响应格式
    API_DEBUG=true                  // 调试模式

JWT 相关

  • 安装 jwt-auth ,参考文档 jwt-auth 文档

    composer require "tymon/jwt-auth"
  • 在 config 目录生成配置文件jwt.php

    php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
  • .env中生成加密所需字符串 JWT_SECRET

    php artisan jwt:secret
  • 修改你的 app/Models/User.php

    <?php
    
    namespace App\Models;
    
    use Illuminate\Contracts\Auth\MustVerifyEmail;
    use Illuminate\Database\Eloquent\Factories\HasFactory;
    use Illuminate\Foundation\Auth\User as Authenticatable;
    use Illuminate\Notifications\Notifiable;
    use Tymon\JWTAuth\Contracts\JWTSubject;
    
    class User extends Authenticatable implements JWTSubject
    {
      use HasFactory, Notifiable;
    
      /**
       * The attributes that are mass assignable.
       *
       * @var array
       */
      protected $fillable = [
          'name',
          'email',
          'password',
      ];
    
      /**
       * The attributes that should be hidden for arrays.
       *
       * @var array
       */
      protected $hidden = [
          'password',
          'remember_token',
      ];
    
      /**
       * The attributes that should be cast to native types.
       *
       * @var array
       */
      protected $casts = [
          'email_verified_at' => 'datetime',
      ];
    
      public function getJWTIdentifier()
      {
          return $this->getKey();
      }
    
      public function getJWTCustomClaims()
      {
          return [];
      }
    }
    
  • 修改 config/auth.php

    
    'defaults' => [
      'guard' => 'api',
      'passwords' => 'users',
    ],
    
    ...
    
    'guards' => [
      'api' => [
          'driver' => 'jwt',
          'provider' => 'users',
      ],
    ],
    
  • 修改 routes/api.php , 由Dingo 接管

    <?php
    
    $api = app('Dingo\Api\Routing\Router');
    
    $api->version('v1', ['namespace' => 'App\Http\Controllers\Api\v1'], function ($api) {
      $api->get('password', 'AuthController@password')->name('password');
      $api->post('login', 'AuthController@login')->name('login');
      $api->group(['middleware' => 'api.auth'], function ($api) {
          $api->post('logout', 'AuthController@logout');
          $api->post('refresh', 'AuthController@refresh');
          $api->post('me', 'AuthController@me')->name('me');
      });
    });
    
  • 创建 基础控制器、用户认证控制器,对应路由文件中的命名空间 App\Http\Controllers\Api\v1

    • Controller

      <?php
      namespace App\Http\Controllers\Api\V1;
      
      use Dingo\Api\Routing\Helpers;
      use Illuminate\Routing\Controller as BaseController;
      
      class Controller extends BaseController
      {
      use Helpers;
      }
      
    • AuthController

      <?php
      
      namespace App\Http\Controllers\Api\V1;
      
      use App\Models\User;
      
      class AuthController extends Controller
      {
      public function password()
      {
          $password = bcrypt('123456');
          User::find(1)->update(['password' => $password]);
          return response()->json(['password' => 123456]);
      }
      /**
       * Get a JWT via given credentials.
       *
       * @return \Illuminate\Http\JsonResponse
       */
      public function login()
      {
          $credentials = request(['email', 'password']);
      
          if (! $token = auth()->attempt($credentials)) {
              return response()->json(['error' => 'Unauthorized'], 401);
          }
      
          return $this->respondWithToken($token);
      }
      
      /**
       * Get the authenticated User.
       *
       * @return \Illuminate\Http\JsonResponse
       */
      public function me()
      {
          return response()->json(auth()->user());
      }
      
      /**
       * Log the user out (Invalidate the token).
       *
       * @return \Illuminate\Http\JsonResponse
       */
      public function logout()
      {
          auth()->logout();
      
          return response()->json(['message' => 'Successfully logged out']);
      }
      
      /**
       * Refresh a token.
       *
       * @return \Illuminate\Http\JsonResponse
       */
      public function refresh()
      {
          return $this->respondWithToken(auth()->refresh());
      }
      
      /**
       * Get the token array structure.
       *
       * @param  string $token
       *
       * @return \Illuminate\Http\JsonResponse
       */
      protected function respondWithToken($token)
      {
          return response()->json([
              'access_token' => $token,
              'token_type' => 'bearer',
              'expires_in' => auth()->factory()->getTTL() * 60
          ]);
      }
      }
      

配置 Dongo API 的Auth认证使用JWT

  • config/api.php

      'auth' => [
          'jwt' => 'Dingo\Api\Auth\Provider\JWT',
      ],

认证测试

  • 密码错误时
    auth.png
  • 更新并获取数据库ID=1的密码用于测试
    password.png
  • 用正确的密码尝试获取access_token
    access_token.png
  • access_token 获取用户信息
    user.png

当你不想使用默认的email 作为用户名时

editParams.png

  • 测试
    name.png

访问节流限制

  • Dingo API 默认节流限速是绑定客户 ip 地址的。如果需要自定义节流限速方法,需要注册你自己的解决者。
  • 新建 app/Http/Middleware/MyThrottle.php, 例如我这里以openid为标识节流限速

    <?php
    
    namespace App\Http\Middleware;
    
    use Dingo\Api\Contract\Http\RateLimit\HasRateLimiter;
    use Dingo\Api\Http\RateLimit\Throttle\Throttle;
    use Dingo\Api\Http\Request;
    use Illuminate\Container\Container;
    
    class MyThrottle extends Throttle implements HasRateLimiter
    {
      /**
       * @param Container $container
       * @return bool
       */
      public function match(Container $container): bool
      {
          return $container['api.auth']->check();
      }
    
      public function getRateLimiter(Container $app, Request $request): string
      {
          $user = auth()->user();
          return $user->openid;    
      }
    }
    
  • 在 routes/api.php 使用

    <?php
    
    use App\Http\Middleware\MyThrottle;
    
    $api = app('Dingo\Api\Routing\Router');
    
    $api->version('v1', ['namespace' => 'App\Http\Controllers\Api\v1'], function ($api) {
      $api->group(['middleware' => 'api.auth'], function ($api) {
          $api->get('limit', [
              'middleware' => 'api.throttle',
              'throttle' => new MyThrottle(['limit' => 1, 'expires' => 1]), //一分钟一次请求
              'uses' => 'AuthController@limit'
          ])->name('limit');
      });
    });
    
  • 测试

    • 同一分钟请求第一次

    limit-pass.png

    • 同一分钟请求第二次

    limit-err.png

  • 当然你如果认为Dingdo APi 抛出的异常不美观,你也可以捕获 Dongo API 错误进行自定义配置

    • 首先在 app/Exceptions/Dingo.php 文件用于处理自定义
    <?php
    
    <?php
    
    namespace App\Exceptions;
    
    use Exception;
    use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
    
    class Dingo extends ExceptionHandler
    {
      /**
       * Render an exception into an HTTP response.
       *
       * @param  \Illuminate\Http\Request  $request
       * @param  \Exception  $exception
       * @return \Illuminate\Http\Response
       */
      public function render($request, Exception|\Throwable $exception)
      {
          if (get_class($exception) == 'Dingo\Api\Exception\RateLimitExceededException') {
              return response()->json([
                  'message' => '请勿频繁提交!!'
              ],429);
          }
          return parent::render($request, $exception);
      }
    }
    • app/Providers/AppServiceProvider.php 中注册服务
    <?php
    
    namespace App\Providers;
    
    use Illuminate\Http\Request;
    use Illuminate\Support\ServiceProvider;
    
    class AppServiceProvider extends ServiceProvider
    {
      /**
       * Register any application services.
       *
       * @return void
       */
      public function register()
      {
          //
      }
    
      /**
       * Bootstrap any application services.
       *
       * @return void
       */
      public function boot()
      {
          app('api.exception')->register(function (\Exception $exception) {
              return app('App\Exceptions\Dingo')->render(
                  Request::capture(),
                  $exception
              );
          });
      }
    }
    

    error.png

Last modification:June 19, 2023
如果觉得我的文章对你有用,请随意赞赏