laravel 中配置DingoApi 和JWT

2023-10-21 13:55:51 浏览数 (1)

laravel 相关

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

代码语言:javascript复制
composer create-project --prefer-dist laravel/laravel laravel "8.5.*"

.env文件中配置数据库连接

代码语言:javascript复制
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=De5RJZWSjJF42FkC

数据库迁移

代码语言:javascript复制
# 在项目根目录执行
php artisan migrate

数据填充

填充前准备

执行填充

代码语言:javascript复制
php artisan db:seed

Dingo Api 相关

安装 Dingo Api

代码语言:javascript复制
composer require "dingo/api"

在 config 目录生成配置文件api.php

代码语言:javascript复制
php artisan vendor:publish --provider="DingoApiProviderLaravelServiceProvider"

.env文件里配置Dingo Api , Dingo API 配置项说明

代码语言:javascript复制
# 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 文档

代码语言:javascript复制
composer require "tymon/jwt-auth"

在 config 目录生成配置文件jwt.php

代码语言:javascript复制
php artisan vendor:publish --provider="TymonJWTAuthProvidersLaravelServiceProvider"

.env中生成加密所需字符串 JWT_SECRET

代码语言:javascript复制
php artisan jwt:secret

修改你的 app/Models/User.php

代码语言:javascript复制
<?php

namespace AppModels;

use IlluminateContractsAuthMustVerifyEmail;
use IlluminateDatabaseEloquentFactoriesHasFactory;
use IlluminateFoundationAuthUser as Authenticatable;
use IlluminateNotificationsNotifiable;
use TymonJWTAuthContractsJWTSubject;

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

代码语言:javascript复制

'defaults' => [
  'guard' => 'api',
  'passwords' => 'users',
],

...

'guards' => [
  'api' => [
      'driver' => 'jwt',
      'provider' => 'users',
  ],
],

修改 routes/api.php , 由Dingo 接管

代码语言:javascript复制
<?php

$api = app('DingoApiRoutingRouter');

$api->version('v1', ['namespace' => 'AppHttpControllersApiv1'], 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');
  });
});

创建 基础控制器、用户认证控制器,对应路由文件中的命名空间 AppHttpControllersApiv1

Controller

代码语言:javascript复制
<?php
namespace AppHttpControllersApiV1;

use DingoApiRoutingHelpers;
use IlluminateRoutingController as BaseController;

class Controller extends BaseController
{
use Helpers;
}

AuthController

代码语言:javascript复制
<?php

namespace AppHttpControllersApiV1;

use AppModelsUser;

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 IlluminateHttpJsonResponse
 */
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 IlluminateHttpJsonResponse
 */
public function me()
{
    return response()->json(auth()->user());
}

/**
 * Log the user out (Invalidate the token).
 *
 * @return IlluminateHttpJsonResponse
 */
public function logout()
{
    auth()->logout();

    return response()->json(['message' => 'Successfully logged out']);
}

/**
 * Refresh a token.
 *
 * @return IlluminateHttpJsonResponse
 */
public function refresh()
{
    return $this->respondWithToken(auth()->refresh());
}

/**
 * Get the token array structure.
 *
 * @param  string $token
 *
 * @return IlluminateHttpJsonResponse
 */
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

代码语言:javascript复制
  'auth' => [
      'jwt' => 'DingoApiAuthProviderJWT',
  ],

认证测试

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

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

  • 测试

访问节流限制

Dingo API 默认节流限速是绑定客户 ip 地址的。如果需要自定义节流限速方法,需要注册你自己的解决者。

新建 app/Http/Middleware/MyThrottle.php, 例如我这里以openid为标识节流限速

代码语言:javascript复制
<?php

namespace AppHttpMiddleware;

use DingoApiContractHttpRateLimitHasRateLimiter;
use DingoApiHttpRateLimitThrottleThrottle;
use DingoApiHttpRequest;
use IlluminateContainerContainer;

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 使用

代码语言:javascript复制
<?php

use AppHttpMiddlewareMyThrottle;

$api = app('DingoApiRoutingRouter');

$api->version('v1', ['namespace' => 'AppHttpControllersApiv1'], 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');
  });
});

测试

  • 同一分钟请求第一次
  • 同一分钟请求第二次

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

  • 首先在 app/Exceptions/Dingo.php 文件用于处理自定义
代码语言:javascript复制
<?php

<?php

namespace AppExceptions;

use Exception;
use IlluminateFoundationExceptionsHandler as ExceptionHandler;

class Dingo extends ExceptionHandler
{
  /**
   * Render an exception into an HTTP response.
   *
   * @param  IlluminateHttpRequest  $request
   * @param  Exception  $exception
   * @return IlluminateHttpResponse
   */
  public function render($request, Exception|Throwable $exception)
  {
      if (get_class($exception) == 'DingoApiExceptionRateLimitExceededException') {
          return response()->json([
              'message' => '请勿频繁提交!!'
          ],429);
      }
      return parent::render($request, $exception);
  }
}
  • app/Providers/AppServiceProvider.php 中注册服务
代码语言:javascript复制
<?php

namespace AppProviders;

use IlluminateHttpRequest;
use IlluminateSupportServiceProvider;

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('AppExceptionsDingo')->render(
              Request::capture(),
              $exception
          );
      });
  }
}

0 人点赞