• laravel dingo Api结合jwt 处理token校验


    声明:

    1.由于时间有限,本文有很多不足之处,望评论下方留言指正!

    2.本文中代码仅做参考使用,不做实际项目运用,主要是思路,红色部分的注意项要留意!

    3.篇幅较长,注意捡重点看,思路!思路!思路!


    开拔~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


    一、环境说明:

    采用laravel5.8框架,php版本7.2.1

    二、安装

    两种方式,依赖包版本可根据自己实际情况进行调整

    1.在项目目录下composer.json添加依赖包

    #composer.json中
    
    "require": {
          #.......
          "dingo/api": "^2.0",
          "tymon/jwt-auth": "~1.0.0-rc.1",
          #......
    }

    2.直接require 依赖包

    composer requiredingo/api 2.0
    composer require tymon/jwt-auth 1.0.0-rc.1

    三、发布配置文件

    1.发布dingo配置文件

    php artisan vendor:publish --provider="DingoApiProviderLaravelServiceProvider"

    此命令会在 config 目录下生成一个 api.php 配置文件,你可以在此进行自定义配置。

    <?php
    
    
    return [
    
        /*
        |--------------------------------------------------------------------------
        | Standards Tree
        |--------------------------------------------------------------------------
        |
        | Versioning an API with Dingo revolves around content negotiation and
        | custom MIME types. A custom type will belong to one of three
        | standards trees, the Vendor tree (vnd), the Personal tree
        | (prs), and the Unregistered tree (x).
        |
        | By default the Unregistered tree (x) is used, however, should you wish
        | to you can register your type with the IANA. For more details:
        | https://tools.ietf.org/html/rfc6838
        |
        */
    
        'standardsTree' => env('API_STANDARDS_TREE', 'x'),
    
        /*
        |--------------------------------------------------------------------------
        | API Subtype
        |--------------------------------------------------------------------------
        |
        | Your subtype will follow the standards tree you use when used in the
        | "Accept" header to negotiate the content type and version.
        |
        | For example: Accept: application/x.SUBTYPE.v1+json
        |
        */
    
        'subtype' => env('API_SUBTYPE', ''),
    
        /*
        |--------------------------------------------------------------------------
        | Default API Version
        |--------------------------------------------------------------------------
        |
        | This is the default version when strict mode is disabled and your API
        | is accessed via a web browser. It's also used as the default version
        | when generating your APIs documentation.
        |
        */
    
        'version' => env('API_VERSION', 'v1'),
    
        /*
        |--------------------------------------------------------------------------
        | Default API Prefix
        |--------------------------------------------------------------------------
        |
        | A default prefix to use for your API routes so you don't have to
        | specify it for each group.
        |
        */
    
        'prefix' => env('API_PREFIX', 'api'),
    
        'paginate' => [
            'limit' => 15,
        ],
    
        /*
        |--------------------------------------------------------------------------
        | Default API Domain
        |--------------------------------------------------------------------------
        |
        | A default domain to use for your API routes so you don't have to
        | specify it for each group.
        |
        */
    
        'domain' => env('API_DOMAIN', null),
    
        /*
        |--------------------------------------------------------------------------
        | Name
        |--------------------------------------------------------------------------
        |
        | When documenting your API using the API Blueprint syntax you can
        | configure a default name to avoid having to manually specify
        | one when using the command.
        |
        */
    
        'name' => env('API_NAME', null),
    
        /*
        |--------------------------------------------------------------------------
        | Conditional Requests
        |--------------------------------------------------------------------------
        |
        | Globally enable conditional requests so that an ETag header is added to
        | any successful response. Subsequent requests will perform a check and
        | will return a 304 Not Modified. This can also be enabled or disabled
        | on certain groups or routes.
        |
        */
    
        'conditionalRequest' => env('API_CONDITIONAL_REQUEST', true),
    
        /*
        |--------------------------------------------------------------------------
        | Strict Mode
        |--------------------------------------------------------------------------
        |
        | Enabling strict mode will require clients to send a valid Accept header
        | with every request. This also voids the default API version, meaning
        | your API will not be browsable via a web browser.
        |
        */
    
        'strict' => env('API_STRICT', false),
    
        /*
        |--------------------------------------------------------------------------
        | Debug Mode
        |--------------------------------------------------------------------------
        |
        | Enabling debug mode will result in error responses caused by thrown
        | exceptions to have a "debug" key that will be populated with
        | more detailed information on the exception.
        |
        */
    
        'debug' => env('API_DEBUG', false),
    
        /*
        |--------------------------------------------------------------------------
        | Generic Error Format
        |--------------------------------------------------------------------------
        |
        | When some HTTP exceptions are not caught and dealt with the API will
        | generate a generic error response in the format provided. Any
        | keys that aren't replaced with corresponding values will be
        | removed from the final response.
        |
        */
    
        'errorFormat' => [
            'message' => ':message',
            'errors' => ':errors',
            'code' => ':code',
            'status_code' => ':status_code',
            'debug' => ':debug',
        ],
    
        /*
        |--------------------------------------------------------------------------
        | API Middleware
        |--------------------------------------------------------------------------
        |
        | Middleware that will be applied globally to all API requests.
        |
        */
    
        'middleware' => [
    
        ],
    
        /*
        |--------------------------------------------------------------------------
        | Authentication Providers
        |--------------------------------------------------------------------------
        |
        | The authentication providers that should be used when attempting to
        | authenticate an incoming API request.
        |
        */
    
        'auth' => [
            'jwt' => 'DingoApiAuthProviderJWT',
        ],
    
        /*
        |--------------------------------------------------------------------------
        | Throttling / Rate Limiting
        |--------------------------------------------------------------------------
        |
        | Consumers of your API can be limited to the amount of requests they can
        | make. You can create your own throttles or simply change the default
        | throttles.
        |
        */
    
        'throttling' => [
    
        ],
    
        /*
        |--------------------------------------------------------------------------
        | Response Transformer
        |--------------------------------------------------------------------------
        |
        | Responses can be transformed so that they are easier to format. By
        | default a Fractal transformer will be used to transform any
        | responses prior to formatting. You can easily replace
        | this with your own transformer.
        |
        */
    
        'transformer' => env('API_TRANSFORMER', DingoApiTransformerAdapterFractal::class),
    
        /*
        |--------------------------------------------------------------------------
        | Response Formats
        |--------------------------------------------------------------------------
        |
        | Responses can be returned in multiple formats by registering different
        | response formatters. You can also customize an existing response
        | formatter with a number of options to configure its output.
        |
        */
    
        'defaultFormat' => env('API_DEFAULT_FORMAT', 'json'),
    
        'formats' => [
    
            //'json' => DingoApiHttpResponseFormatJson::class,
            #json 返回自定义
            'json' => AppComponentsResponseFormatJson::class,
        ],
    
        'formatsOptions' => [
    
            'json' => [
                'pretty_print' => env('API_JSON_FORMAT_PRETTY_PRINT_ENABLED', false),
                'indent_style' => env('API_JSON_FORMAT_INDENT_STYLE', 'space'),
                'indent_size' => env('API_JSON_FORMAT_INDENT_SIZE', 2),
            ],
    
        ],
    
        /*
         * 接口频率限制
         */
        'rate_limits' => [
            // 访问频率限制,次数/分钟
            'access' => [
                'expires' => env('RATE_LIMITS_EXPIRES', 1),
                'limit'  => env('RATE_LIMITS', 600),
            ],
            // 登录相关,次数/分钟
            'sign' => [
                'expires' => env('SIGN_RATE_LIMITS_EXPIRES', 1),
                'limit'  => env('SIGN_RATE_LIMITS', 10),
            ],
        ],
    
    ];

    2.发布jwt配置文件

    php artisan vendor:publish --provider="TymonJWTAuthProvidersLaravelServiceProvider"

    此命令会在 config 目录下生成一个 jwt.php 配置文件,你可以在此进行自定义配置。

    jwt.php文件

    <?php

    /*
    * This file is part of jwt-auth.
    *
    * (c) Sean Tymon <tymon148@gmail.com>
    *
    * For the full copyright and license information, please view the LICENSE
    * file that was distributed with this source code.
    */

    return [

    /*
    |--------------------------------------------------------------------------
    | JWT Authentication Secret
    |--------------------------------------------------------------------------
    |
    | Don't forget to set this in your .env file, as it will be used to sign
    | your tokens. A helper command is provided for this:
    | `php artisan jwt:secret`
    |
    | Note: This will be used for Symmetric algorithms only (HMAC),
    | since RSA and ECDSA use a private/public key combo (See below).
    |
    | 加密生成 token 的 secret
    */
    'secret' => env('JWT_SECRET'),

    /*
    |--------------------------------------------------------------------------
    | JWT Authentication Keys
    |--------------------------------------------------------------------------
    |
    | The algorithm you are using, will determine whether your tokens are
    | signed with a random string (defined in `JWT_SECRET`) or using the
    | following public & private keys.
    |
    | Symmetric Algorithms:
    | HS256, HS384 & HS512 will use `JWT_SECRET`.
    |
    | Asymmetric Algorithms:
    | RS256, RS384 & RS512 / ES256, ES384 & ES512 will use the keys below.
    |
    | 如果你在 .env 文件中定义了 JWT_SECRET 的随机字符串
    | 那么 jwt 将会使用 对称算法 来生成 token
    | 如果你没有定有,那么jwt 将会使用如下配置的公钥和私钥来生成 token
    */

    'keys' => [

    /*
    |--------------------------------------------------------------------------
    | Public Key
    |--------------------------------------------------------------------------
    |
    | A path or resource to your public key.
    |
    | E.g. 'file://path/to/public/key'
    |
    */

    'public' => env('JWT_PUBLIC_KEY'),

    /*
    |--------------------------------------------------------------------------
    | Private Key
    |--------------------------------------------------------------------------
    |
    | A path or resource to your private key.
    |
    | E.g. 'file://path/to/private/key'
    |
    */

    'private' => env('JWT_PRIVATE_KEY'),

    /*
    |--------------------------------------------------------------------------
    | Passphrase
    |--------------------------------------------------------------------------
    |
    | The passphrase for your private key. Can be null if none set.
    |
    */

    'passphrase' => env('JWT_PASSPHRASE'),

    ],

    /*
    |--------------------------------------------------------------------------
    | JWT time to live
    |--------------------------------------------------------------------------
    |
    | Specify the length of time (in minutes) that the token will be valid for.
    | Defaults to 1 hour.
    |
    | You can also set this to null, to yield a never expiring token.
    | Some people may want this behaviour for e.g. a mobile app.
    | This is not particularly recommended, so make sure you have appropriate
    | systems in place to revoke the token if necessary.
    | Notice: If you set this to null you should remove 'exp' element from 'required_claims' list.

    | 指定 access_token 有效的时间长度(以分钟为单位),默认为1小时,您也可以将其设置为空,以产生永不过期的标记
    */
    'ttl' => env('JWT_TTL', 60),

    /*
    |--------------------------------------------------------------------------
    | Refresh time to live
    |--------------------------------------------------------------------------
    |
    | Specify the length of time (in minutes) that the token can be refreshed
    | within. I.E. The user can refresh their token within a 2 week window of
    | the original token being created until they must re-authenticate.
    | Defaults to 2 weeks.
    |
    | You can also set this to null, to yield an infinite refresh time.
    | Some may want this instead of never expiring tokens for e.g. a mobile app.
    | This is not particularly recommended, so make sure you have appropriate
    | systems in place to revoke the token if necessary.
    |
    | access_token 可刷新的时间长度(以分钟为单位)。默认的时间为 2 周。
    | 用法:如果用户有一个 access_token,那么他可以带着他的 access_token
    | 过来领取新的 access_token,直到 2 周的时间后,他便无法继续刷新了,需要重新登录。
    */

    'refresh_ttl' => env('JWT_REFRESH_TTL', 20160),

    /*
    |--------------------------------------------------------------------------
    | JWT hashing algorithm
    |--------------------------------------------------------------------------
    |
    | Specify the hashing algorithm that will be used to sign the token.
    |
    | See here: https://github.com/namshi/jose/tree/master/src/Namshi/JOSE/Signer/OpenSSL
    | for possible values.
    | 指定将用于对令牌进行签名的散列算法。
    */

    'algo' => env('JWT_ALGO', 'HS256'),

    /*
    |--------------------------------------------------------------------------
    | Required Claims
    |--------------------------------------------------------------------------
    |
    | Specify the required claims that must exist in any token.
    | A TokenInvalidException will be thrown if any of these claims are not
    | present in the payload.
    |
    |指定必须存在于任何令牌中的声明。
    */

    'required_claims' => [
    'iss',
    'iat',
    'exp',
    'nbf',
    'sub',
    'jti',
    ],

    /*
    |--------------------------------------------------------------------------
    | Persistent Claims
    |--------------------------------------------------------------------------
    |
    | Specify the claim keys to be persisted when refreshing a token.
    | `sub` and `iat` will automatically be persisted, in
    | addition to the these claims.
    |
    | Note: If a claim does not exist then it will be ignored.
    |
    |指定在刷新令牌时要保留的声明密钥。
    */

    'persistent_claims' => [
    // 'foo',
    // 'bar',
    ],

    /*
    |--------------------------------------------------------------------------
    | Lock Subject
    |--------------------------------------------------------------------------
    |
    | This will determine whether a `prv` claim is automatically added to
    | the token. The purpose of this is to ensure that if you have multiple
    | authentication models e.g. `AppUser` & `AppOtherPerson`, then we
    | should prevent one authentication request from impersonating another,
    | if 2 tokens happen to have the same id across the 2 different models.
    |
    | Under specific circumstances, you may want to disable this behaviour
    | e.g. if you only have one authentication model, then you would save
    | a little on token size.
    |
    |
    */

    'lock_subject' => true,

    /*
    |--------------------------------------------------------------------------
    | Leeway
    |--------------------------------------------------------------------------
    |
    | This property gives the jwt timestamp claims some "leeway".
    | Meaning that if you have any unavoidable slight clock skew on
    | any of your servers then this will afford you some level of cushioning.
    |
    | This applies to the claims `iat`, `nbf` and `exp`.
    |
    | Specify in seconds - only if you know you need it.
    |
    */

    'leeway' => env('JWT_LEEWAY', 0),

    /*
    |--------------------------------------------------------------------------
    | Blacklist Enabled
    |--------------------------------------------------------------------------
    |
    | In order to invalidate tokens, you must have the blacklist enabled.
    | If you do not want or need this functionality, then set this to false.
    |
    | 为了使令牌无效,您必须启用黑名单。如果不想或不需要此功能,请将其设置为 false。
    */

    'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true),

    /*
    | -------------------------------------------------------------------------
    | Blacklist Grace Period
    | -------------------------------------------------------------------------
    |
    | When multiple concurrent requests are made with the same JWT,
    | it is possible that some of them fail, due to token regeneration
    | on every request.
    |
    | Set grace period in seconds to prevent parallel request failure.
    |
    | 当多个并发请求使用相同的JWT进行时,由于 access_token 的刷新 ,其中一些可能会失败,以秒为单位设置请求时间以防止并发的请求失败。
    */

    'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0),

    /*
    |--------------------------------------------------------------------------
    | Cookies encryption
    |--------------------------------------------------------------------------
    |
    | By default Laravel encrypt cookies for security reason.
    | If you decide to not decrypt cookies, you will have to configure Laravel
    | to not encrypt your cookie token by adding its name into the $except
    | array available in the middleware "EncryptCookies" provided by Laravel.
    | see https://laravel.com/docs/master/responses#cookies-and-encryption
    | for details.
    |
    | Set it to true if you want to decrypt cookies.
    |
    */

    'decrypt_cookies' => false,

    /*
    |--------------------------------------------------------------------------
    | Providers
    |--------------------------------------------------------------------------
    |
    | Specify the various providers used throughout the package.
    |
    | 指定整个包中使用的各种提供程序。
    */

    'providers' => [

    /*
    |--------------------------------------------------------------------------
    | JWT Provider
    |--------------------------------------------------------------------------
    |
    | Specify the provider that is used to create and decode the tokens.
    |
    | 用于创建和解码令牌的提供程序。
    */

    'jwt' => TymonJWTAuthProvidersJWTLcobucci::class,

    /*
    |--------------------------------------------------------------------------
    | Authentication Provider
    |--------------------------------------------------------------------------
    |
    | Specify the provider that is used to authenticate users.
    |
    | 用于对用户进行身份验证的提供程序。
    */

    'auth' => TymonJWTAuthProvidersAuthIlluminate::class,

    /*
    |--------------------------------------------------------------------------
    | Storage Provider
    |--------------------------------------------------------------------------
    |
    | Specify the provider that is used to store tokens in the blacklist.
    |
    | 用于在黑名单中存储标记的提供程序。
    */

    'storage' => TymonJWTAuthProvidersStorageIlluminate::class,

    ],

    ];

    3.jwt生成秘钥

    jwt-auth已经预先定义好了一个 Artisan 命令方便你生成 Secret,通过下面命令生成

    php artisan jwt:secret

    该命令会在你的 .env 文件中新增一行 JWT_SECRET=secret,如下所示

    #.env
    JWT_SECRET=HSKxIUfwCdJj5ewdbqfQo5im9zj3r5g9

    4.注册中间件

    JWT 认证扩展包附带了允许我们使用的中间件。在 app/Http/Kernel.php 中注册 auth.jwt 中间件:

    protected $routeMiddleware = [
        ....
        'auth.jwt' => TymonJWTAuthHttpMiddlewareAuthenticate::class,
    ];

    5.设置路由,调整routes/api.php文件,和下方第“七、控制器创建” 对应

    <?php
    $api = app('DingoApiRoutingRouter');
    # 示例1
    $api->version('v1', function ($api) {
        $api->get('demo', function () {
            return 'hello world';
        });
    
    });
    # 示例2
    $api->version('v2', [
        'namespace' => 'AppHttpControllersApiV2',
        'middleware' => 'serializer:array',
    ], function($api) {
        $api->group([
            'middleware' => ['api.throttle','global.log'],
            'limit' => config('api.rate_limits.sign.limit'),#接口访问限制
            'expires' => config('api.rate_limits.sign.expires'),
        ], function($api){
            # 无需校验token的接口
            //......
             $api->post('login', 'AuthController@login')->name('api.auth.login');
            // 需要 token 验证的接口
            $api->group(['middleware' => ['auth.jwt']], function($api) {
                    $api->post('login', 'AuthController@login')->name('api.auth.login');
                    $api->post('logout', 'AuthController@logout')->name('api.auth.logout');
                    $api->post('refresh', 'AuthController@refresh')->name('api.auth.refresh');
                    $api->post('me', 'AuthController@me')->name('api.auth.me');
              //......
            });
        });
    
    });
    ?>

     

    四、配置调整

    1.添加配置(config/app.php)

    'providers' => [
           ......
           DingoApiProviderLaravelServiceProvider::class,
           TymonJWTAuthProvidersLaravelServiceProvider::class,
    ],
    'aliases' => [
    ......
          'JWTAuth' => TymonJWTAuthFacadesJWTAuth::class,
    'JWTFactory' => TymonJWTAuthFacadesJWTFactory::class,
    ]

    2.修改auth.php

    'guards' => [
            'web' => [
                'driver' => 'session',
                'provider' => 'users',
            ],
    
            'api' => [
                'driver' => 'jwt',#把此处驱动改为jwt,默认为laravel框架自带的驱动token
                'provider' => 'users',//注意此处根据自己的实际情况进行调整
    ],
    ]
    ,
    'providers' => [
    'users' => [
    'driver' => 'eloquent',
    'model' => AppModelsUser::class,//注意此处根据自己的实际情况进行调整
    ],

    // 'users' => [
    // 'driver' => 'database',
    // 'table' => 'users',
    // ],
    ],

    3 .env文件

    #Dingo
    # 公开的及商业项目用 vnd
    API_STANDARDS_TREE=prs
    #项目简称
    API_SUBTYPE=dingo-demo
    #域名
    API_DOMAIN=
    # 前缀
    API_PREFIX=api
    #不提供版本时使用的版本号
    API_VERSION=v3
    #开启 debug 模式
    API_DEBUG=true
    API_DEFAULT_FORMAT=json
    #Jwt
    #
    jwt秘钥 JWT_SECRET=HSKxIUfwCdJj5ewdbqfQo5im9zj3r5g9
    #jwt有效时间,单位:分钟 JWT_TTL=60

    五、更新 User 模型

    使用默认的 User 表来生成 token

    JWT 需要在 User 模型中实现 TymonJWTAuthContractsJWTSubject 接口。 此接口需要实现两个方法  getJWTIdentifier 和 getJWTCustomClaims。使用以下内容更新 app/User.php 。

    <?php
    
    namespace App;
    
    use IlluminateFoundationAuthUser as Authenticatable;
    use IlluminateNotificationsNotifiable;
    use TymonJWTAuthContractsJWTSubject;
    
    class User extends Authenticatable implements JWTSubject
    {
        use 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',
        ];
    
        /**
         * Get the identifier that will be stored in the subject claim of the JWT.
         *
         * @return mixed
         */
        public function getJWTIdentifier()
        {
            return $this->getKey();
        }
    
        /**
         * Return a key value array, containing any custom claims to be added to the JWT.
         *
         * @return array
         */
        public function getJWTCustomClaims()
        {
            return [];
        }
    }

    六、JWT 身份验证逻辑

    使用 JWT 身份验证在 laravel 中写 Restful API 的逻辑。

    用户注册时需要姓名,邮箱和密码。那么,让我们创建一个表单请求来验证数据。通过运行以下命令创建名为AuthorizationRequest的表单请求:

    php artisan make:request  ApiAuthorizationRequest

    它将在 app/Http/Requests/Api 目录下创建 AuthorizationRequest文件。

    <?php
    
    namespace AppHttpRequestsApi;
    
    class AuthorizationRequest extends Request
    {
    
        /**
         * 确定是否授权用户发出此请求
         *
         * @return bool
         */
        public function authorize()
        {
            return true;
        }
    
        /**

    * 获取应用于请求的验证规则 * Get the validation rules that apply to the request. * * @return array
    */ public function rules() { return [ 'username' => 'required|string', 'password' => 'required|string|min:6', ]; } }

    七、控制器创建

    官方案例,稍作了修改: login登录,me获取用户信息,logout退出登录,refresh刷新token,respondWithToken返回token

    <?php
    namespace AppHttpControllers;
    
    use use AppHttpRequestsApiAuthorizationRequest;
    class AuthController extends Controller { /** * Create a new AuthController instance. * 要求附带email和password(数据来源users表) * @return void */ public function __construct() { // 这里额外注意了:官方文档样例中只除外了『login』 // 这样的结果是,token 只能在有效期以内进行刷新,过期无法刷新 // 如果把 refresh 也放进去,token 即使过期但仍在刷新期以内也可刷新 // 不过刷新一次作废 $this->middleware('auth:api', ['except' => ['login']]); // 另外关于上面的中间件,官方文档写的是『auth:api』 // 但是我推荐用 『jwt.auth』,效果是一样的,但是有更加丰富的报错信息返回 } /** * Get a JWT via given credentials. * @param AuthorizationRequest $request * @return IlluminateHttpJsonResponse */ public function login(AuthorizationRequest $request) { $credentials = request(['email', 'password']); if (! $token = auth('api')->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('api')->user()); } /** * Log the user out (Invalidate the token). * * @return IlluminateHttpJsonResponse */ public function logout() { auth('api')->logout(); return response()->json(['message' => 'Successfully logged out']); } /** * Refresh a token. * 刷新token,如果开启黑名单,以前的token便会失效。 * 值得注意的是用上面的getToken再获取一次Token并不算做刷新,两次获得的Token是并行的,即两个都可用。 * @return IlluminateHttpJsonResponse */ public function refresh() { return $this->respondWithToken(auth('api')->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('api')->factory()->getTTL() * 60 ]); } }

    注意:

    jwt使用

    jwt koken两种使用方式

    1.加到 url 中:?token=你的token

    2.加到 header 中,建议用这种,因为在 https 情况下更安全:Authorization:Bearer 你的token

    八、自定义Dingo Api 响应格式

    1.新建Json.php文件,AppComponentsResponseFormatJson.php, 代码示例如下:

    主要思路就是继承DingoApiHttpResponseFormatJson类,并进行重写

    <?php
    
    namespace AppComponentsResponseFormat;
    
    use AppComponentsResultsCodeSuccessCode;
    use DingoApiHttpResponseFormatJson as DingoJson;
    
    class Json extends DingoJson
    {
        /**
         * Encode the content to its JSON representation.
         *
         * @param mixed $content
         *
         * @return string
         */
        protected function encode($content)
        {
            $jsonEncodeOptions = [];
    
            // Here is a place, where any available JSON encoding options, that
            // deal with users' requirements to JSON response formatting and
            // structure, can be conveniently applied to tweak the output.
    
            if ($this->isJsonPrettyPrintEnabled()) {
                $jsonEncodeOptions[] = JSON_PRETTY_PRINT;
                $jsonEncodeOptions[] = JSON_UNESCAPED_UNICODE;
            }
    
    #主要在此处进行调整 $newContent = [ 'code' => $content['code'] ?? SuccessCode::SUCCESS, 'data' => $content['data'] ?? [], 'message' => $content['message'] ?? SuccessCode::SUCCESS_MSG, ]; $encodedString = $this->performJsonEncoding($newContent, $jsonEncodeOptions); if ($this->isCustomIndentStyleRequired()) { $encodedString = $this->indentPrettyPrintedJson( $encodedString, $this->options['indent_style'] ); } return $encodedString; } }

    注意:由于自定义了响应返回,所以"七、控制器创建"的示例代码中需要调整格式为,需要有data键

     /**
         * 响应 Token 结构体
         *
         * @param $token
         * @return mixed
         */
        protected function respondWithToken($token)
        {
            $res = [
                'data' => [
                    'token' => $token,
                    'token_type' => 'Bearer',
                    'expires_in' => Auth::guard('api')->factory()->getTTL()
                ]
            ];
    
            return $this->response->array($res);
        }

    2.在config/api.php文件中,调整json返回类

    #config/api.php
    'formats' => [
    
            //'json' => DingoApiHttpResponseFormatJson::class,
            #json 返回自定义
            'json' => AppComponentsResponseFormatJson::class,
        ],

    九、自定义Dingo 异常返回

    1.新建API异常处理文件AppExceptionsApiHandler,具体实现根据自己需要,此处代码仅做参考,注意:文件里面有自定义的code码,另外该文件只是示例,可根据自己需要进行调整

    <?php
    
    namespace AppExceptions;
    
    use AppComponentsResultsCodeAuthCode;
    use AppComponentsResultsCodeCommonCode;
    use AppComponentsResultsCodeErrorCode;
    use Exception;
    use IlluminateFoundationExceptionsHandler as ExceptionHandler;
    use InterventionImageExceptionNotFoundException;
    use SymfonyComponentHttpKernelExceptionUnauthorizedHttpException;
    use SymfonyComponentHttpKernelExceptionMethodNotAllowedHttpException;
    use AppComponentsResultsExceptionServiceErrorException;
    use AppComponentsResultsExceptionServiceException;
    use AppComponentsResultsExceptionServiceLogicException;
    use AppComponentsResultsExceptionServiceValidException;
    use TymonJWTAuthExceptionsJWTException;
    use TymonJWTAuthExceptionsTokenBlacklistedException;
    #该目录下面的几个文件,在下面有示例,可根据情况自行调整
    use AppComponentsResultsResults;
    
    class ApiHandler extends ExceptionHandler
    {
        /**
         * A list of the exception types that are not reported.
         *
         * @var array
         */
        protected $dontReport = [
            //
        ];
    
        /**
         * A list of the inputs that are never flashed for validation exceptions.
         *
         * @var array
         */
        protected $dontFlash = [
            'password',
            'password_confirmation',
        ];
    
        /**
         * Report or log an exception.
         *
         * @param  Exception  $exception
         * @return void
         */
        public function report(Exception $exception)
        {
            parent::report($exception);
        }
    
        /**
         * Render an exception into an HTTP response.
         *
         * @param  IlluminateHttpRequest  $request
         * @param  Exception  $exception
         * @return  AppComponentsResultsResult | IlluminateHttpResponse
         * @see https://learnku.com/docs/dingo-api/2.0.0/Errors-And-Error-Responses/1447
         */
        public function render($request, Exception $exception)
        {
            if ($request->isJson()) {
                $class = get_class($exception);
                switch ($class) {
                    case 'DingoApiExceptionValidationHttpException':
                        return Results::failure(AuthCode::MISSING_ACCESS_TOKEN_MSG, AuthCode::MISSING_ACCESS_TOKEN);
                    case 'SymfonyComponentHttpKernelExceptionUnauthorizedHttpException':
                    case 'TymonJWTAuthExceptionsJWTException'://Token could not be parsed from the request.
                    case 'TymonJWTAuthExceptionsTokenBlacklistedException'://The token has been blackliste
                        return Results::failure(AuthCode::MISSING_ACCESS_TOKEN_MSG, AuthCode::MISSING_ACCESS_TOKEN);
                    case 'SymfonyComponentHttpKernelExceptionNotFoundHttpException':
                        return Results::failure(CommonCode::URL_NOT_FOUND_MSG, CommonCode::URL_NOT_FOUND);
                    case 'AppComponentsResultsExceptionServiceValidException':
                    case 'AppComponentsResultsExceptionServiceLogicException':
                    case 'AppComponentsResultsExceptionServiceErrorException':
                    case 'AppComponentsResultsExceptionServiceException':
                        return Results::failure($exception->getMessage(), $exception->getErrorCode());
                    case 'SymfonyComponentHttpKernelExceptionMethodNotAllowedHttpException':
                        return Results::failure($exception->getMessage(), $exception->getCode());
                    case 'DingoApiExceptionRateLimitExceededException':
                        return Results::failure(CommonCode::IRATE_LIMIT_REQUEST_MSG, CommonCode::RATE_LIMIT_REQUEST);
                    default:
                        return Results::error(ErrorCode::UNKNOWN_ERROR_MSG, ErrorCode::UNKNOWN_ERROR);
                }
            }
    
            if(config('app.debug')){
                return parent::render($request, $exception);
            }
    
            if (method_exists($exception, 'getStatusCode')) {
                $statusCode = $exception->getStatusCode();
                switch ($statusCode) {
                    case 400:
                    case 403:
                    case 405:
                        return response()->view('errors.404', ['message'=>$exception->getMessage()], $exception->getStatusCode());
                        break;
                    case 500:
                    case 501:
                    case 502:
                        return response()->view('errors.500', ['message'=>$exception->getMessage()], $exception->getStatusCode());
                        break;
                    default:
                        return response()->view('errors.404', ['message'=>$exception->getMessage()], $exception->getStatusCode());
                        break;
                }
            }
    
            return parent::render($request, $exception);
        }
    
    }

    CommonCode.php代码示例:

    <?php
    
    namespace AppComponentsResultsCode;
    
    /**
     * 公共的业务异常错误码
     * Class CommonCode
     * @package AppComponentsResultsCode
     */
    class CommonCode
    {
        const INVALID_ARGS = "C_1";
        const INVALID_ARGS_MSG = "参数无效";
    
        const DATA_NOT_FOUND = "C_2";
        const DATA_NOT_FOUND_MSG = "无数据";
        //......
    }

    Results.php文件示例:

    <?php
    
    namespace AppComponentsResults;
    
    use AppComponentsResultsCodeCommonCode;
    use AppComponentsResultsCodeErrorCode;
    use AppComponentsResultsCodeSuccessCode;
    
    final class Results
    {
        /**
         * 成功
         * @param mixed data 并设置data参数
         * @param string $code 错误码
         * @return Result
         */
        public static function success($data = null,$code=SuccessCode::SUCCESS)
        {
            return new Result(SuccessCode::SUCCESS_MSG, $code, $data);
        }

       /**
    * 失败
    * @param string $message 失败信息
    * @param string $code 失败编码
    * @return Result 返回对象
    */
    public static function failure(string $message, string $code) {
    return new Result($message, $code);
    }
    }
    Result.php文件示例
    <?php
    
    namespace AppComponentsResults;
    
    use AppComponentsResultsCodeErrorCode;
    use AppComponentsResultsCodeSuccessCode;
    use AppComponentsResultsCodeErrorCode;
    
    /**
     * Class Result
     */
    class Result
    {
    
        public $code;
        public $message;
        public $data;
    
        public function __construct($message, $code, $data = null)
        {
            $this->message = $message;
            $this->code = $code;
            $this->data = $data;
        }
    
        /**
         * 获取错误码
         * @return string 错误码
         */
        function getCode(): string
        {
            return $this->code;
        }
    
        /**
         * 获取成功或错误的信息
         * @return string 成功或错误的信息
         */
        function getMessage(): string
        {
            return $this->message;
        }
    
        /**
         * 获取数据
         * @return object 数据
         */
        function getData()
        {
            return $this->data;
        }
    
        /**
         * 设置错误码
         * @param string code 错误码
         * @return Result Result对象
         */
        function setCode(string $code): Result
        {
            $this->code = $code;
            return $this;
        }
    
        /**
         * 设置成功或错误的信息
         * @param string message 成功或错误的信息
         * @return Result
         */
        function setMessage(string $message): Result
        {
            $this->message = $message;
            return $this;
        }
    
        /**
         * 设置数据
         * @param mixed data 数据
         * @return Result
         */
        function setData($data): Result
        {
            $this->data = $data;
            return $this;
        }
    
        function __toString()
        {
            return json_encode($this, Constants::DEFAULT_ENCODING_OPTIONS | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
        }
    
        function jsonSerialize()
        {
            return $this;
        }
    }

     2.在 App/Providers/AppServiceProvider中注册新的异常处理

    <?php
    
    namespace AppProviders;
    
    use IlluminateSupportServiceProvider;
    
    class AppServiceProvider extends ServiceProvider
    {
        /**
         * Bootstrap any application services.
         *
         * @return void
         */
        public function boot()
        {
            app('api.exception')->register(function (Exception $exception) {
                $request = IlluminateHttpRequest::capture();
                return app('AppExceptionsApiHandler')->render($request, $exception);
            });
        }
       
        /**
         * Register any application services.
         *
         * @return void
         */
        public function register()
        {
         //......
        }
    
    }

    参考文档:

    https://learnku.com/docs/dingo-api/2.0.0/Configuration/1444#6cdca8

    https://learnku.com/docs/laravel/5.8/api-authentication/3952

    https://learnku.com/laravel/t/27760

  • 相关阅读:
    python之天气爬虫
    python之一异常问题(TypeError: object of type 'NoneType' has no len())
    数据分析之漏斗分析
    python之pytest_addoption : 命令行参数
    python之一driver.find_element_by_xpath与driver.find_element(by, value)的区别
    python之正则表达式从列表中取值报类型错误
    python之append和extend的区别
    pyton之字典的使用
    python之pd.DataFrame函数使用
    python之正则表达式 re.findall 用法
  • 原文地址:https://www.cnblogs.com/joshua317/p/14667978.html
Copyright © 2020-2023  润新知