在 Laravel 6、Module、Lighthouse 中实现 安全 验证 的流程(使用验证器类来支持复杂的验证规则)

1、当请求响应成功时的结构。如图1

当请求响应成功时的结构。

图1


mutation {
  onlineStoreThemeAssetCreate(
    input: { themeId: "vogue", content: "string", key: "string" }
  ) {
    themeAsset {
      id
      themeId
      content
      key
      mimeType
      category
      schema
      createdAt
      updatedAt
      deletable
      renameable
      updatable
    }
  }
}

{
  "data": {
    "onlineStoreThemeAssetCreate": {
      "themeAsset": {
        "id": "653",
        "themeId": "vogue",
        "content": "string",
        "key": "string",
        "mimeType": "text/x-php",
        "category": "unknown",
        "schema": null,
        "createdAt": "2022-02-25 01:49:53",
        "updatedAt": "2022-02-25 01:49:53",
        "deletable": false,
        "renameable": false,
        "updatable": false
      }
    }
  }
}

2、但是,现阶段并未针对请求参数进行安全验证。参考:https://lighthouse-php.com/master/security/validation.html#single-arguments 。Lighthouse 允许您在查询和变更中使用 Laravel 的验证。如图2

但是,现阶段并未针对请求参数进行安全验证。参考:https://lighthouse-php.com/master/security/validation.html#single-arguments 。Lighthouse 允许您在查询和变更中使用 Laravel 的验证。

图2

3、此 GraphQL API 的变更虽然在输入对象中仅有 3 个参数,但是验证规则比较复杂。包含如下规则:
(1)验证表中是否存在 themeId: “vogue” 的记录,如果不存在,验证失败;

SELECT * FROM `wshop_store`.`theme_asset` WHERE `theme_id` = 'vogue' LIMIT 0,1

(2)验证 key: “string” 的格式,其必须为一个有效的相对路径,例:assets/iconfont/iconfont.css。其值等于 string ,验证失败;
(3)验证 key: “string” 的格式,其为一个有效的相对路径后,其文件后缀名必须属于一个预先定义的数组中,例:[‘json’, ‘css’, ‘js’];
(4)验证表中 themeId: “vogue”, key: “string” 记录的唯一性,如果已存在,验证失败;

SELECT * FROM `wshop_store`.`theme_asset` WHERE `theme_id` = 'vogue' AND `asset_key` = 'string' LIMIT 0,1

(5)验证 content: “string” 的格式,例:当 key 的值的文件后缀名为 .json 时,需要验证 content 的值格式为 json 格式;
(6)mime_type 的值需要基于 key 的值转换得出;
(7)category 的值需要基于 key 的值转换得出;

4、使用验证器类来支持复杂的验证规则。Lighthouse 对验证器类使用简单的命名约定,只需使用输入类型的名称并附加 Validator 。最终生成文件:/app/GraphQL/Validators/CreateOnlineStoreThemeAssetInputValidator.php

PS E:/wwwroot/lighthouse-tutorial> php artisan lighthouse:validator CreateOnlineStoreThemeAssetInputValidator
Validator created successfully.

5、由于现在需要在 Module ThemeStore 中使用此验证器类,将文件:/app/GraphQL/Validators/CreateOnlineStoreThemeAssetInputValidator.php 剪切至:/Modules/ThemeStore/Validators/CreateOnlineStoreThemeAssetInputValidator.php

<?php

namespace Modules/ThemeStore/Validators;

use Nuwave/Lighthouse/Validation/Validator;

class CreateOnlineStoreThemeAssetInputValidator extends Validator
{
    /**
     * Return the validation rules.
     *
     * @return array<string, array<mixed>>
     */
    public function rules(): array
    {
        return [
            'themeId' => [
                'exists:theme_asset,theme_id'
            ],
        ];
    }
}

6、编辑文件:/Modules/ThemeStore/Resources/graphql/theme_asset.graphql,修改输入对象,使用 @validator 指令,以指定验证器类

input OnlineStoreThemeAssetCreateInput @validator(class: "Modules//ThemeStore//Validators//CreateOnlineStoreThemeAssetInputValidator") {
    "主题ID"
    themeId: String!,
    "内容"
    content: String!,
    "路径,相对于主题的路径,如 pages/index.blade.php"
    key: String!,
}

7、测试验证规则是否有效,确定有效。如图3

测试验证规则是否有效,确定有效。

图3


mutation {
  onlineStoreThemeAssetCreate(
    input: { themeId: "vogue1", content: "string", key: "string" }
  ) {
    themeAsset {
      id
      themeId
      content
      key
      mimeType
      category
      schema
      createdAt
      updatedAt
      deletable
      renameable
      updatable
    }
  }
}

{
  "errors": [
    {
      "message": "Validation failed for the field [onlineStoreThemeAssetCreate].",
      "extensions": {
        "validation": {
          "input.themeId": [
            "The selected input.theme id is invalid."
          ]
        },
        "category": "validation"
      },
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "onlineStoreThemeAssetCreate"
      ],
      "trace": [
        {
          "file": "E://wwwroot//wshop//platform//vendor//nuwave//lighthouse//src//Schema//Directives//ArgTraversalDirective.php",
          "line": 29,
          "call": "Nuwave//Lighthouse//Validation//ValidateDirective::Nuwave//Lighthouse//Validation//{closure}(null, array(1), instance of Nuwave//Lighthouse//Schema//Context, instance of GraphQL//Type//Definition//ResolveInfo)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//nuwave//lighthouse//src//Schema//Directives//TrimDirective.php",
          "line": 56,
          "call": "Nuwave//Lighthouse//Schema//Directives//ArgTraversalDirective::Nuwave//Lighthouse//Schema//Directives//{closure}(null, array(1), instance of Nuwave//Lighthouse//Schema//Context, instance of GraphQL//Type//Definition//ResolveInfo)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//nuwave//lighthouse//src//Schema//Factories//FieldFactory.php",
          "line": 99,
          "call": "Nuwave//Lighthouse//Schema//Directives//TrimDirective::Nuwave//Lighthouse//Schema//Directives//{closure}(null, array(1), instance of Nuwave//Lighthouse//Schema//Context, instance of GraphQL//Type//Definition//ResolveInfo)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//webonyx//graphql-php//src//Executor//ReferenceExecutor.php",
          "line": 623,
          "call": "Nuwave//Lighthouse//Schema//Factories//FieldFactory::Nuwave//Lighthouse//Schema//Factories//{closure}(null, array(1), instance of Nuwave//Lighthouse//Schema//Context, instance of GraphQL//Type//Definition//ResolveInfo)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//webonyx//graphql-php//src//Executor//ReferenceExecutor.php",
          "line": 550,
          "call": "GraphQL//Executor//ReferenceExecutor::resolveFieldValueOrError(instance of GraphQL//Type//Definition//FieldDefinition, instance of GraphQL//Language//AST//FieldNode, instance of Closure, null, instance of GraphQL//Type//Definition//ResolveInfo)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//webonyx//graphql-php//src//Executor//ReferenceExecutor.php",
          "line": 474,
          "call": "GraphQL//Executor//ReferenceExecutor::resolveField(GraphQLType: Mutation, null, instance of ArrayObject(1), array(1))"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//webonyx//graphql-php//src//Executor//ReferenceExecutor.php",
          "line": 857,
          "call": "GraphQL//Executor//ReferenceExecutor::GraphQL//Executor//{closure}(array(0), 'onlineStoreThemeAssetCreate')"
        },
        {
          "call": "GraphQL//Executor//ReferenceExecutor::GraphQL//Executor//{closure}(array(0), 'onlineStoreThemeAssetCreate')"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//webonyx//graphql-php//src//Executor//ReferenceExecutor.php",
          "line": 859,
          "function": "array_reduce(array(1), instance of Closure, array(0))"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//webonyx//graphql-php//src//Executor//ReferenceExecutor.php",
          "line": 490,
          "call": "GraphQL//Executor//ReferenceExecutor::promiseReduce(array(1), instance of Closure, array(0))"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//webonyx//graphql-php//src//Executor//ReferenceExecutor.php",
          "line": 263,
          "call": "GraphQL//Executor//ReferenceExecutor::executeFieldsSerially(GraphQLType: Mutation, null, array(0), instance of ArrayObject(1))"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//webonyx//graphql-php//src//Executor//ReferenceExecutor.php",
          "line": 215,
          "call": "GraphQL//Executor//ReferenceExecutor::executeOperation(instance of GraphQL//Language//AST//OperationDefinitionNode, null)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//webonyx//graphql-php//src//Executor//Executor.php",
          "line": 156,
          "call": "GraphQL//Executor//ReferenceExecutor::doExecute()"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//webonyx//graphql-php//src//GraphQL.php",
          "line": 162,
          "call": "GraphQL//Executor//Executor::promiseToExecute(instance of GraphQL//Executor//Promise//Adapter//SyncPromiseAdapter, instance of GraphQL//Type//Schema, instance of GraphQL//Language//AST//DocumentNode, null, instance of Nuwave//Lighthouse//Schema//Context, array(0), null, null)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//webonyx//graphql-php//src//GraphQL.php",
          "line": 94,
          "call": "GraphQL//GraphQL::promiseToExecute(instance of GraphQL//Executor//Promise//Adapter//SyncPromiseAdapter, instance of GraphQL//Type//Schema, instance of GraphQL//Language//AST//DocumentNode, null, instance of Nuwave//Lighthouse//Schema//Context, array(0), null, null, array(29))"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//nuwave//lighthouse//src//GraphQL.php",
          "line": 268,
          "call": "GraphQL//GraphQL::executeQuery(instance of GraphQL//Type//Schema, instance of GraphQL//Language//AST//DocumentNode, null, instance of Nuwave//Lighthouse//Schema//Context, array(0), null, null, array(29))"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//nuwave//lighthouse//src//GraphQL.php",
          "line": 203,
          "call": "Nuwave//Lighthouse//GraphQL::executeParsedQuery(instance of GraphQL//Language//AST//DocumentNode, instance of Nuwave//Lighthouse//Schema//Context, array(0), null, null)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//nuwave//lighthouse//src//GraphQL.php",
          "line": 162,
          "call": "Nuwave//Lighthouse//GraphQL::parseAndExecuteQuery('mutation {/n  onlineStoreThemeAssetCreate(/n    input: { themeId: /"vogue1/", content: /"string/", key: /"string/" }/n  ) {/n    themeAsset {/n      id/n      themeId/n      content/n      key/n      mimeType/n      category/n      schema/n      createdAt/n      updatedAt/n      deletable/n      renameable/n      updatable/n    }/n  }/n}', instance of Nuwave//Lighthouse//Schema//Context, array(0), null, null)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//nuwave//lighthouse//src//GraphQL.php",
          "line": 121,
          "call": "Nuwave//Lighthouse//GraphQL::executeOperation(instance of GraphQL//Server//OperationParams, instance of Nuwave//Lighthouse//Schema//Context)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//nuwave//lighthouse//src//Support//Utils.php",
          "line": 99,
          "call": "Nuwave//Lighthouse//GraphQL::Nuwave//Lighthouse//{closure}(instance of GraphQL//Server//OperationParams)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//nuwave//lighthouse//src//GraphQL.php",
          "line": 120,
          "call": "Nuwave//Lighthouse//Support//Utils::applyEach(instance of Closure, instance of GraphQL//Server//OperationParams)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//nuwave//lighthouse//src//Support//Http//Controllers//GraphQLController.php",
          "line": 32,
          "call": "Nuwave//Lighthouse//GraphQL::executeOperationOrOperations(instance of GraphQL//Server//OperationParams, instance of Nuwave//Lighthouse//Schema//Context)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Routing//ControllerDispatcher.php",
          "line": 48,
          "call": "Nuwave//Lighthouse//Support//Http//Controllers//GraphQLController::__invoke(instance of Illuminate//Http//Request, instance of Nuwave//Lighthouse//GraphQL, instance of Illuminate//Events//Dispatcher, instance of Laragraph//Utils//RequestParser, instance of Nuwave//Lighthouse//Execution//SingleResponse, instance of Nuwave//Lighthouse//Execution//ContextFactory)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Routing//Route.php",
          "line": 219,
          "call": "Illuminate//Routing//ControllerDispatcher::dispatch(instance of Illuminate//Routing//Route, instance of Nuwave//Lighthouse//Support//Http//Controllers//GraphQLController, '__invoke')"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Routing//Route.php",
          "line": 176,
          "call": "Illuminate//Routing//Route::runController()"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Routing//Router.php",
          "line": 681,
          "call": "Illuminate//Routing//Route::run()"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Pipeline//Pipeline.php",
          "line": 130,
          "call": "Illuminate//Routing//Router::Illuminate//Routing//{closure}(instance of Illuminate//Http//Request)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//nuwave//lighthouse//src//Support//Http//Middleware//AttemptAuthentication.php",
          "line": 34,
          "call": "Illuminate//Pipeline//Pipeline::Illuminate//Pipeline//{closure}(instance of Illuminate//Http//Request)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Pipeline//Pipeline.php",
          "line": 171,
          "call": "Nuwave//Lighthouse//Support//Http//Middleware//AttemptAuthentication::handle(instance of Illuminate//Http//Request, instance of Closure)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//nuwave//lighthouse//src//Support//Http//Middleware//AcceptJson.php",
          "line": 27,
          "call": "Illuminate//Pipeline//Pipeline::Illuminate//Pipeline//{closure}(instance of Illuminate//Http//Request)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Pipeline//Pipeline.php",
          "line": 171,
          "call": "Nuwave//Lighthouse//Support//Http//Middleware//AcceptJson::handle(instance of Illuminate//Http//Request, instance of Closure)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Pipeline//Pipeline.php",
          "line": 105,
          "call": "Illuminate//Pipeline//Pipeline::Illuminate//Pipeline//{closure}(instance of Illuminate//Http//Request)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Routing//Router.php",
          "line": 683,
          "call": "Illuminate//Pipeline//Pipeline::then(instance of Closure)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Routing//Router.php",
          "line": 658,
          "call": "Illuminate//Routing//Router::runRouteWithinStack(instance of Illuminate//Routing//Route, instance of Illuminate//Http//Request)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Routing//Router.php",
          "line": 624,
          "call": "Illuminate//Routing//Router::runRoute(instance of Illuminate//Http//Request, instance of Illuminate//Routing//Route)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Routing//Router.php",
          "line": 613,
          "call": "Illuminate//Routing//Router::dispatchToRoute(instance of Illuminate//Http//Request)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Foundation//Http//Kernel.php",
          "line": 170,
          "call": "Illuminate//Routing//Router::dispatch(instance of Illuminate//Http//Request)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Pipeline//Pipeline.php",
          "line": 130,
          "call": "Illuminate//Foundation//Http//Kernel::Illuminate//Foundation//Http//{closure}(instance of Illuminate//Http//Request)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//barryvdh//laravel-debugbar//src//Middleware//InjectDebugbar.php",
          "line": 67,
          "call": "Illuminate//Pipeline//Pipeline::Illuminate//Pipeline//{closure}(instance of Illuminate//Http//Request)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Pipeline//Pipeline.php",
          "line": 171,
          "call": "Barryvdh//Debugbar//Middleware//InjectDebugbar::handle(instance of Illuminate//Http//Request, instance of Closure)"
        },
        {
          "file": "E://wwwroot//wshop//platform//app//Http//Middleware//ChangeAppUrlMiddleware.php",
          "line": 23,
          "call": "Illuminate//Pipeline//Pipeline::Illuminate//Pipeline//{closure}(instance of Illuminate//Http//Request)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Pipeline//Pipeline.php",
          "line": 171,
          "call": "App//Http//Middleware//ChangeAppUrlMiddleware::handle(instance of Illuminate//Http//Request, instance of Closure)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Foundation//Http//Middleware//TransformsRequest.php",
          "line": 21,
          "call": "Illuminate//Pipeline//Pipeline::Illuminate//Pipeline//{closure}(instance of Illuminate//Http//Request)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Pipeline//Pipeline.php",
          "line": 171,
          "call": "Illuminate//Foundation//Http//Middleware//TransformsRequest::handle(instance of Illuminate//Http//Request, instance of Closure)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Foundation//Http//Middleware//TransformsRequest.php",
          "line": 21,
          "call": "Illuminate//Pipeline//Pipeline::Illuminate//Pipeline//{closure}(instance of Illuminate//Http//Request)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Pipeline//Pipeline.php",
          "line": 171,
          "call": "Illuminate//Foundation//Http//Middleware//TransformsRequest::handle(instance of Illuminate//Http//Request, instance of Closure)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Foundation//Http//Middleware//ValidatePostSize.php",
          "line": 27,
          "call": "Illuminate//Pipeline//Pipeline::Illuminate//Pipeline//{closure}(instance of Illuminate//Http//Request)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Pipeline//Pipeline.php",
          "line": 171,
          "call": "Illuminate//Foundation//Http//Middleware//ValidatePostSize::handle(instance of Illuminate//Http//Request, instance of Closure)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Foundation//Http//Middleware//CheckForMaintenanceMode.php",
          "line": 63,
          "call": "Illuminate//Pipeline//Pipeline::Illuminate//Pipeline//{closure}(instance of Illuminate//Http//Request)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Pipeline//Pipeline.php",
          "line": 171,
          "call": "Illuminate//Foundation//Http//Middleware//CheckForMaintenanceMode::handle(instance of Illuminate//Http//Request, instance of Closure)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//fideloper//proxy//src//TrustProxies.php",
          "line": 57,
          "call": "Illuminate//Pipeline//Pipeline::Illuminate//Pipeline//{closure}(instance of Illuminate//Http//Request)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Pipeline//Pipeline.php",
          "line": 171,
          "call": "Fideloper//Proxy//TrustProxies::handle(instance of Illuminate//Http//Request, instance of Closure)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//dingo//api//src//Http//Middleware//Request.php",
          "line": 111,
          "call": "Illuminate//Pipeline//Pipeline::Illuminate//Pipeline//{closure}(instance of Illuminate//Http//Request)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Pipeline//Pipeline.php",
          "line": 171,
          "call": "Dingo//Api//Http//Middleware//Request::handle(instance of Illuminate//Http//Request, instance of Closure)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Pipeline//Pipeline.php",
          "line": 105,
          "call": "Illuminate//Pipeline//Pipeline::Illuminate//Pipeline//{closure}(instance of Illuminate//Http//Request)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Foundation//Http//Kernel.php",
          "line": 145,
          "call": "Illuminate//Pipeline//Pipeline::then(instance of Closure)"
        },
        {
          "file": "E://wwwroot//wshop//platform//vendor//laravel//framework//src//Illuminate//Foundation//Http//Kernel.php",
          "line": 110,
          "call": "Illuminate//Foundation//Http//Kernel::sendRequestThroughRouter(instance of Illuminate//Http//Request)"
        },
        {
          "file": "E://wwwroot//wshop//platform//public//index.php",
          "line": 57,
          "call": "Illuminate//Foundation//Http//Kernel::handle(instance of Illuminate//Http//Request)"
        }
      ]
    }
  ],
  "data": {
    "onlineStoreThemeAssetCreate": null
  }
}

8、查看 Laravel Telescope 中请求中的 SQL 语句。如图4

查看 Laravel Telescope 中请求中的 SQL 语句。

图4

select count(*) as aggregate from `theme_asset` where `theme_id` = 'vogue1'

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/250728.html

(0)
上一篇 2022年5月1日
下一篇 2022年5月1日

相关推荐

发表回复

登录后才能评论