laravel 中配置DingoApi 和JWT

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

数据填充

填充前准备

DatabaseSeeder.png

执行填充

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

Dingo Api 相关

安装 Dingo Api

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

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

代码语言:javascript
复制
php artisan vendor:publish --provider="Dingo\Api\Provider\LaravelServiceProvider"

.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="Tymon\JWTAuth\Providers\LaravelServiceProvider"

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

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

修改你的 app/Models/User.php

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

代码语言:javascript
复制

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

...

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

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

代码语言:javascript
复制
<?php

$api = app('Dingo\Api\Routing\Router');

api-&gt;version(&#39;v1&#39;, [&#39;namespace&#39; =&gt; &#39;App\Http\Controllers\Api\v1&#39;], function (api) {
$api->get('password', 'AuthController@password')->name('password');
$api->post('login', 'AuthController@login')->name('login');
api-&gt;group([&#39;middleware&#39; =&gt; &#39;api.auth&#39;], 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

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

代码语言:javascript
复制
<?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()-&gt;attempt(credentials)) {
    return response()->json(['error' => 'Unauthorized'], 401);
    }

    return this-&gt;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

代码语言:javascript
复制
  '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为标识节流限速

代码语言:javascript
复制
<?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 使用

代码语言:javascript
复制
<?php

use App\Http\Middleware\MyThrottle;

$api = app('Dingo\Api\Routing\Router');

api-&gt;version(&#39;v1&#39;, [&#39;namespace&#39; =&gt; &#39;App\Http\Controllers\Api\v1&#39;], function (api) {
api-&gt;group([&#39;middleware&#39; =&gt; &#39;api.auth&#39;], 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 文件用于处理自定义
代码语言:javascript
复制
<?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 中注册服务
代码语言:javascript
复制
<?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