This is an automated email from the ASF dual-hosted git repository. github-bot pushed a commit to branch asf-site in repository https://gitbox.apache.org/repos/asf/apisix-website.git
The following commit(s) were added to refs/heads/asf-site by this push: new 8441a0a Deploy to GitHub pages 8441a0a is described below commit 8441a0a1b56fa79461a31d8d9910118a65b7c875 Author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> AuthorDate: Sun Dec 27 09:10:20 2020 +0000 Deploy to GitHub pages --- apisix/plugin-develop.html | 160 +++++++++++++++++++++------ apisix/plugin-develop/index.html | 160 +++++++++++++++++++++------ apisix/zh-cn/install-dependencies.html | 6 +- apisix/zh-cn/install-dependencies/index.html | 6 +- apisix/zh-cn/plugin-develop.html | 152 +++++++++++++++++++------ apisix/zh-cn/plugin-develop/index.html | 152 +++++++++++++++++++------ 6 files changed, 498 insertions(+), 138 deletions(-) diff --git a/apisix/plugin-develop.html b/apisix/plugin-develop.html index 95fe625..242695a 100644 --- a/apisix/plugin-develop.html +++ b/apisix/plugin-develop.html @@ -31,6 +31,8 @@ <li><a href="#choose-phase-to-run"><strong>choose phase to run</strong></a></li> <li><a href="#implement-the-logic"><strong>implement the logic</strong></a></li> <li><a href="#write-test-case"><strong>write test case</strong></a></li> +<li><a href="#register-public-api"><strong>register public API</strong></a></li> +<li><a href="#register-control-api"><strong>register control API</strong></a></li> </ul> <h2><a class="anchor" aria-hidden="true" id="check-dependencies"></a><a href="#check-dependencies" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22- [...] <p>if you have dependencies on external libraries, check the dependent items. if your plugin needs to use shared memory, it @@ -54,23 +56,22 @@ method "http_init" in the file <strong>apisix.lua</strong>, And you ma configuration file in <strong>bin/apisix</strong> file. but it is easy to have an impact on the overall situation according to the existing plugin mechanism, we do not recommend this unless you have a complete grasp of the code.</p> <h2><a class="anchor" aria-hidden="true" id="name-and-config"></a><a href="#name-and-config" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 [...] -<p>determine the name and priority of the plugin, and add to conf/config-default.yaml. For example, for the key-auth plugin, +<p>Determine the name and priority of the plugin, and add to conf/config-default.yaml. For example, for the example-plugin plugin, you need to specify the plugin name in the code (the name is the unique identifier of the plugin and cannot be -duplicate), you can see the code in file "<strong>apisix/plugins/key-auth.lua</strong>" :</p> -<pre><code class="hljs css language-lua"> <span class="hljs-keyword">local</span> plugin_name = <span class="hljs-string">"key-auth"</span> +duplicate), you can see the code in file "<strong>apisix/plugins/example-plugin.lua</strong>" :</p> +<pre><code class="hljs css language-lua"><span class="hljs-keyword">local</span> plugin_name = <span class="hljs-string">"example-plugin"</span> - <span class="hljs-keyword">local</span> _M = { - version = <span class="hljs-number">0.1</span>, - priority = <span class="hljs-number">2500</span>, - <span class="hljs-built_in">type</span> = <span class="hljs-string">'auth'</span>, - name = plugin_name, - schema = schema, - } -</code></pre> -<p>Note : The priority of the new plugin cannot be the same as the priority of any existing plugin. In addition, plugins with a high priority value will be executed first. For example, the priority of basic-auth is 2520 and the priority of ip-restriction is 3000. Therefore, the ip-restriction plugin will be executed first, then the basic-auth plugin.</p> +<span class="hljs-keyword">local</span> _M = { + version = <span class="hljs-number">0.1</span>, + priority = <span class="hljs-number">0</span>, + name = plugin_name, + schema = schema, + metadata_schema = metadata_schema, +} +</code></pre> +<p>Note : The priority of the new plugin cannot be the same as the priority of any existing plugin. In addition, plugins with a high priority value will be executed first in a given phase (see the definition of <code>phase</code> in <a href="#choose-phase-to-run">choose-phase-to-run</a>). For example, the priority of example-plugin is 0 and the priority of ip-restriction is 3000. Therefore, the ip-restriction plugin will be executed first, then the example-plugin plugin.</p> <p>in the "<strong>conf/config-default.yaml</strong>" configuration file, the enabled plugins (all specified by plugin name) are listed.</p> <pre><code class="hljs css language-yaml"><span class="hljs-attr">plugins:</span> <span class="hljs-comment"># plugin list</span> - <span class="hljs-bullet">-</span> <span class="hljs-string">example-plugin</span> <span class="hljs-bullet">-</span> <span class="hljs-string">limit-req</span> <span class="hljs-bullet">-</span> <span class="hljs-string">limit-count</span> <span class="hljs-bullet">-</span> <span class="hljs-string">limit-conn</span> @@ -86,6 +87,7 @@ duplicate), you can see the code in file "<strong>apisix/plugins/key-auth.l <span class="hljs-bullet">-</span> <span class="hljs-string">openid-connect</span> <span class="hljs-bullet">-</span> <span class="hljs-string">proxy-rewrite</span> <span class="hljs-bullet">-</span> <span class="hljs-string">redirect</span> + <span class="hljs-string">...</span> </code></pre> <p>Note : the order of the plugins is not related to the order of execution.</p> <p>If your plugin has a new code directory of its own, you will need to modify the <code>Makefile</code> to create directory, such as:</p> @@ -93,25 +95,32 @@ duplicate), you can see the code in file "<strong>apisix/plugins/key-auth.l <span class="hljs-variable">$(</span>INSTALL) apisix/plugins/skywalking/*.lua <span class="hljs-variable">$(</span>INST_LUADIR)/apisix/plugins/skywalking/ </code></pre> <h2><a class="anchor" aria-hidden="true" id="schema-and-check"></a><a href="#schema-and-check" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2. [...] -<p>Write <a href="https://json-schema.org">Json Schema</a> descriptions and check functions. similarly, take the key-auth plugin as an example to see its +<p>Write <a href="https://json-schema.org">Json Schema</a> descriptions and check functions. Similarly, take the example-plugin plugin as an example to see its configuration data :</p> -<pre><code class="hljs css language-json"> "key-auth" : { - "key" : "auth-one" - } -</code></pre> -<p>The configuration data of the plugin is relatively simple. Only one attribute named key is supported. Let's look -at its schema description :</p> -<pre><code class="hljs css language-lua"> <span class="hljs-keyword">local</span> schema = { - <span class="hljs-built_in">type</span> = <span class="hljs-string">"object"</span>, - properties = { - key = {<span class="hljs-built_in">type</span> = <span class="hljs-string">"string"</span>}, - } - } +<pre><code class="hljs css language-json">"example-plugin" : { + "i": 1, + "s": "s", + "t": [1] +} +</code></pre> +<p>Let's look at its schema description :</p> +<pre><code class="hljs css language-lua"><span class="hljs-keyword">local</span> schema = { + <span class="hljs-built_in">type</span> = <span class="hljs-string">"object"</span>, + properties = { + i = {<span class="hljs-built_in">type</span> = <span class="hljs-string">"number"</span>, minimum = <span class="hljs-number">0</span>}, + s = {<span class="hljs-built_in">type</span> = <span class="hljs-string">"string"</span>}, + t = {<span class="hljs-built_in">type</span> = <span class="hljs-string">"array"</span>, minItems = <span class="hljs-number">1</span>}, + ip = {<span class="hljs-built_in">type</span> = <span class="hljs-string">"string"</span>}, + port = {<span class="hljs-built_in">type</span> = <span class="hljs-string">"integer"</span>}, + }, + required = {<span class="hljs-string">"i"</span>}, +} </code></pre> +<p>The schema defines a non-negative number <code>i</code>, a string <code>s</code>, a non-empty array of <code>t</code>, and <code>ip</code> / <code>port</code>. Only <code>i</code> is required.</p> <p>At the same time, we need to implement the <strong>check_schema(conf)</strong> method to complete the specification verification.</p> -<pre><code class="hljs css language-lua"> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.check_schema</span><span class="hljs-params">(conf)</span></span> - <span class="hljs-keyword">return</span> core.schema.check(schema, conf) - <span class="hljs-keyword">end</span> +<pre><code class="hljs css language-lua"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.check_schema</span><span class="hljs-params">(conf, schema_type)</span></span> + <span class="hljs-keyword">return</span> core.schema.check(schema, conf) +<span class="hljs-keyword">end</span> </code></pre> <p>Note: the project has provided the public method "<strong>core.schema.check</strong>", which can be used directly to complete JSON verification.</p> @@ -136,13 +145,52 @@ verification.</p> metadata_schema = metadata_schema, } </code></pre> +<p>You might have noticed the key-auth plugin has <code>type = 'auth'</code> in its definition. +When we set the type of plugin to <code>auth</code>, it means that this plugin is an authentication plugin.</p> +<p>An authentication plugin needs to choose a consumer after execution. For example, in key-auth plugin, it calls the <code>consumer.attach_consumer</code> to attach a consumer, which is chosen via the <code>apikey</code> header.</p> +<p>To interact with the <code>consumer</code> resource, this type of plugin needs to provide a <code>consumer_schema</code> to check the <code>plugins</code> configuration in the <code>consumer</code>.</p> +<p>Here is the consumer configuration for key-auth plugin:</p> +<pre><code class="hljs css language-json">{ + <span class="hljs-attr">"username"</span>: <span class="hljs-string">"Joe"</span>, + <span class="hljs-attr">"plugins"</span>: { + <span class="hljs-attr">"key-auth"</span>: { + <span class="hljs-attr">"key"</span>: <span class="hljs-string">"Joe's key"</span> + } + } +} +</code></pre> +<p>It will be used when you try to create a <a href="https://github.com/apache/apisix/blob/master/doc/admin-api.md#consumer">Consumer</a></p> +<p>To validate the configuration, the plugin uses a schema like this:</p> +<pre><code class="hljs css language-json">local consumer_schema = { + type = "object", + additionalProperties = false, + properties = { + key = {type = "string"}, + }, + required = {"key"}, +} +</code></pre> +<p>Note the difference between key-auth's <strong>check_schema(conf)</strong> method to example-plugin's:</p> +<pre><code class="hljs css language-lua"><span class="hljs-comment">-- key-auth</span> +<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.check_schema</span><span class="hljs-params">(conf, schema_type)</span></span> + <span class="hljs-keyword">if</span> schema_type == core.schema.TYPE_CONSUMER <span class="hljs-keyword">then</span> + <span class="hljs-keyword">return</span> core.schema.check(consumer_schema, conf) + <span class="hljs-keyword">else</span> + <span class="hljs-keyword">return</span> core.schema.check(schema, conf) + <span class="hljs-keyword">end</span> +<span class="hljs-keyword">end</span> +</code></pre> +<pre><code class="hljs css language-lua"><span class="hljs-comment">-- example-plugin</span> +<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.check_schema</span><span class="hljs-params">(conf, schema_type)</span></span> + <span class="hljs-keyword">return</span> core.schema.check(schema, conf) +<span class="hljs-keyword">end</span> +</code></pre> <h2><a class="anchor" aria-hidden="true" id="choose-phase-to-run"></a><a href="#choose-phase-to-run" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.2 [...] <p>Determine which phase to run, generally access or rewrite. If you don't know the <a href="https://openresty-reference.readthedocs.io/en/latest/Directives/">Openresty life cycle</a>, it's recommended to know it in advance. For example key-auth is an authentication plugin, thus the authentication should be completed -before forwarding the request to any upstream service. Therefore, the plugin can be executed in the rewrite and access phases. -In APISIX, the authentication logic is implemented in the rewrite phase. Generally, IP access and interface -permission are completed in the access phase.</p> -<p>The following code snippet shows how to implement any logic relevant to the plugin in the Openresty log phase.</p> +before forwarding the request to any upstream service. Therefore, the plugin must be executed in the rewrite phases. +In APISIX, only the authentication logic can be run in the rewrite phase. Other logic needs to run before proxy should be in access phase.</p> +<p>The following code snippet shows how to implement any logic relevant to the plugin in the OpenResty log phase.</p> <pre><code class="hljs css language-lua"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.log</span><span class="hljs-params">(conf)</span></span> <span class="hljs-comment">-- Implement logic here</span> <span class="hljs-keyword">end</span> @@ -161,7 +209,7 @@ of the real test case. For example, the key-auth plugin :</p> location /t { content_by_lua_block { <span class="hljs-keyword">local</span> plugin = <span class="hljs-keyword">require</span>(<span class="hljs-string">"apisix.plugins.key-auth"</span>) - <span class="hljs-keyword">local</span> ok, err = plugin.check_schema({key = <span class="hljs-string">'test-key'</span>}) + <span class="hljs-keyword">local</span> ok, err = plugin.check_schema({key = <span class="hljs-string">'test-key'</span>}, core.schema.TYPE_CONSUMER) <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> ok then ngx.say(err) end @@ -192,7 +240,49 @@ are located in the following folder: 't/servroot/logs'.</p> framework will assemble into a complete nginx.conf file. "<strong>t/servroot</strong>" is the working directory of Nginx and start the Nginx instance. according to the information provided by the test case, initiate the http request and check that the return items of HTTP include HTTP status, HTTP response header, HTTP response body and so on.</p> -</span></div></article></div><div class="docs-prevnext"></div></div></div><nav class="onPageNav"><ul class="toc-headings"><li><a href="#check-dependencies">check dependencies</a></li><li><a href="#name-and-config">name and config</a></li><li><a href="#schema-and-check">schema and check</a></li><li><a href="#choose-phase-to-run">choose phase to run</a></li><li><a href="#implement-the-logic">implement the logic</a></li><li><a href="#write-test-case">write test case</a><ul class="toc-headin [...] +<h3><a class="anchor" aria-hidden="true" id="register-public-api"></a><a href="#register-public-api" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.2 [...] +<p>A plugin can register API which exposes to the public. Take jwt-auth plugin as an example, this plugin registers <code>GET /apisix/plugin/jwt/sign</code> to allow client to sign its key:</p> +<pre><code class="hljs css language-lua"><span class="hljs-keyword">local</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">gen_token</span><span class="hljs-params">()</span></span> + ... +<span class="hljs-keyword">end</span> + +<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.api</span><span class="hljs-params">()</span></span> + <span class="hljs-keyword">return</span> { + { + methods = {<span class="hljs-string">"GET"</span>}, + uri = <span class="hljs-string">"/apisix/plugin/jwt/sign"</span>, + handler = gen_token, + } + } +<span class="hljs-keyword">end</span> +</code></pre> +<p>Note that the public API is exposed to the public. +You may need to use <a href="/apisix/plugin-interceptors">interceptors</a> to protect it.</p> +<h3><a class="anchor" aria-hidden="true" id="register-control-api"></a><a href="#register-control-api" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1 [...] +<p>If you only want to expose the API to the localhost or intranet, you can expose it via <a href="/apisix/control-api">Control API</a>.</p> +<p>Take a look at example-plugin plugin:</p> +<pre><code class="hljs css language-lua"><span class="hljs-keyword">local</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hello</span><span class="hljs-params">()</span></span> + <span class="hljs-keyword">local</span> args = ngx.req.get_uri_args() + <span class="hljs-keyword">if</span> args[<span class="hljs-string">"json"</span>] <span class="hljs-keyword">then</span> + <span class="hljs-keyword">return</span> <span class="hljs-number">200</span>, {msg = <span class="hljs-string">"world"</span>} + <span class="hljs-keyword">else</span> + <span class="hljs-keyword">return</span> <span class="hljs-number">200</span>, <span class="hljs-string">"world\n"</span> + <span class="hljs-keyword">end</span> +<span class="hljs-keyword">end</span> + + +<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.control_api</span><span class="hljs-params">()</span></span> + <span class="hljs-keyword">return</span> { + { + methods = {<span class="hljs-string">"GET"</span>}, + uris = {<span class="hljs-string">"/v1/plugin/example-plugin/hello"</span>}, + handler = hello, + } + } +<span class="hljs-keyword">end</span> +</code></pre> +<p>If you don't change the default control API configuration, the plugin will be expose <code>GET /v1/plugin/example-plugin/hello</code> which can only be accessed via <code>127.0.0.1</code>.</p> +</span></div></article></div><div class="docs-prevnext"></div></div></div><nav class="onPageNav"><ul class="toc-headings"><li><a href="#check-dependencies">check dependencies</a></li><li><a href="#name-and-config">name and config</a></li><li><a href="#schema-and-check">schema and check</a></li><li><a href="#choose-phase-to-run">choose phase to run</a></li><li><a href="#implement-the-logic">implement the logic</a></li><li><a href="#write-test-case">write test case</a><ul class="toc-headin [...] document.addEventListener('keyup', function(e) { if (e.target !== document.body) { return; diff --git a/apisix/plugin-develop/index.html b/apisix/plugin-develop/index.html index 95fe625..242695a 100644 --- a/apisix/plugin-develop/index.html +++ b/apisix/plugin-develop/index.html @@ -31,6 +31,8 @@ <li><a href="#choose-phase-to-run"><strong>choose phase to run</strong></a></li> <li><a href="#implement-the-logic"><strong>implement the logic</strong></a></li> <li><a href="#write-test-case"><strong>write test case</strong></a></li> +<li><a href="#register-public-api"><strong>register public API</strong></a></li> +<li><a href="#register-control-api"><strong>register control API</strong></a></li> </ul> <h2><a class="anchor" aria-hidden="true" id="check-dependencies"></a><a href="#check-dependencies" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22- [...] <p>if you have dependencies on external libraries, check the dependent items. if your plugin needs to use shared memory, it @@ -54,23 +56,22 @@ method "http_init" in the file <strong>apisix.lua</strong>, And you ma configuration file in <strong>bin/apisix</strong> file. but it is easy to have an impact on the overall situation according to the existing plugin mechanism, we do not recommend this unless you have a complete grasp of the code.</p> <h2><a class="anchor" aria-hidden="true" id="name-and-config"></a><a href="#name-and-config" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 [...] -<p>determine the name and priority of the plugin, and add to conf/config-default.yaml. For example, for the key-auth plugin, +<p>Determine the name and priority of the plugin, and add to conf/config-default.yaml. For example, for the example-plugin plugin, you need to specify the plugin name in the code (the name is the unique identifier of the plugin and cannot be -duplicate), you can see the code in file "<strong>apisix/plugins/key-auth.lua</strong>" :</p> -<pre><code class="hljs css language-lua"> <span class="hljs-keyword">local</span> plugin_name = <span class="hljs-string">"key-auth"</span> +duplicate), you can see the code in file "<strong>apisix/plugins/example-plugin.lua</strong>" :</p> +<pre><code class="hljs css language-lua"><span class="hljs-keyword">local</span> plugin_name = <span class="hljs-string">"example-plugin"</span> - <span class="hljs-keyword">local</span> _M = { - version = <span class="hljs-number">0.1</span>, - priority = <span class="hljs-number">2500</span>, - <span class="hljs-built_in">type</span> = <span class="hljs-string">'auth'</span>, - name = plugin_name, - schema = schema, - } -</code></pre> -<p>Note : The priority of the new plugin cannot be the same as the priority of any existing plugin. In addition, plugins with a high priority value will be executed first. For example, the priority of basic-auth is 2520 and the priority of ip-restriction is 3000. Therefore, the ip-restriction plugin will be executed first, then the basic-auth plugin.</p> +<span class="hljs-keyword">local</span> _M = { + version = <span class="hljs-number">0.1</span>, + priority = <span class="hljs-number">0</span>, + name = plugin_name, + schema = schema, + metadata_schema = metadata_schema, +} +</code></pre> +<p>Note : The priority of the new plugin cannot be the same as the priority of any existing plugin. In addition, plugins with a high priority value will be executed first in a given phase (see the definition of <code>phase</code> in <a href="#choose-phase-to-run">choose-phase-to-run</a>). For example, the priority of example-plugin is 0 and the priority of ip-restriction is 3000. Therefore, the ip-restriction plugin will be executed first, then the example-plugin plugin.</p> <p>in the "<strong>conf/config-default.yaml</strong>" configuration file, the enabled plugins (all specified by plugin name) are listed.</p> <pre><code class="hljs css language-yaml"><span class="hljs-attr">plugins:</span> <span class="hljs-comment"># plugin list</span> - <span class="hljs-bullet">-</span> <span class="hljs-string">example-plugin</span> <span class="hljs-bullet">-</span> <span class="hljs-string">limit-req</span> <span class="hljs-bullet">-</span> <span class="hljs-string">limit-count</span> <span class="hljs-bullet">-</span> <span class="hljs-string">limit-conn</span> @@ -86,6 +87,7 @@ duplicate), you can see the code in file "<strong>apisix/plugins/key-auth.l <span class="hljs-bullet">-</span> <span class="hljs-string">openid-connect</span> <span class="hljs-bullet">-</span> <span class="hljs-string">proxy-rewrite</span> <span class="hljs-bullet">-</span> <span class="hljs-string">redirect</span> + <span class="hljs-string">...</span> </code></pre> <p>Note : the order of the plugins is not related to the order of execution.</p> <p>If your plugin has a new code directory of its own, you will need to modify the <code>Makefile</code> to create directory, such as:</p> @@ -93,25 +95,32 @@ duplicate), you can see the code in file "<strong>apisix/plugins/key-auth.l <span class="hljs-variable">$(</span>INSTALL) apisix/plugins/skywalking/*.lua <span class="hljs-variable">$(</span>INST_LUADIR)/apisix/plugins/skywalking/ </code></pre> <h2><a class="anchor" aria-hidden="true" id="schema-and-check"></a><a href="#schema-and-check" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2. [...] -<p>Write <a href="https://json-schema.org">Json Schema</a> descriptions and check functions. similarly, take the key-auth plugin as an example to see its +<p>Write <a href="https://json-schema.org">Json Schema</a> descriptions and check functions. Similarly, take the example-plugin plugin as an example to see its configuration data :</p> -<pre><code class="hljs css language-json"> "key-auth" : { - "key" : "auth-one" - } -</code></pre> -<p>The configuration data of the plugin is relatively simple. Only one attribute named key is supported. Let's look -at its schema description :</p> -<pre><code class="hljs css language-lua"> <span class="hljs-keyword">local</span> schema = { - <span class="hljs-built_in">type</span> = <span class="hljs-string">"object"</span>, - properties = { - key = {<span class="hljs-built_in">type</span> = <span class="hljs-string">"string"</span>}, - } - } +<pre><code class="hljs css language-json">"example-plugin" : { + "i": 1, + "s": "s", + "t": [1] +} +</code></pre> +<p>Let's look at its schema description :</p> +<pre><code class="hljs css language-lua"><span class="hljs-keyword">local</span> schema = { + <span class="hljs-built_in">type</span> = <span class="hljs-string">"object"</span>, + properties = { + i = {<span class="hljs-built_in">type</span> = <span class="hljs-string">"number"</span>, minimum = <span class="hljs-number">0</span>}, + s = {<span class="hljs-built_in">type</span> = <span class="hljs-string">"string"</span>}, + t = {<span class="hljs-built_in">type</span> = <span class="hljs-string">"array"</span>, minItems = <span class="hljs-number">1</span>}, + ip = {<span class="hljs-built_in">type</span> = <span class="hljs-string">"string"</span>}, + port = {<span class="hljs-built_in">type</span> = <span class="hljs-string">"integer"</span>}, + }, + required = {<span class="hljs-string">"i"</span>}, +} </code></pre> +<p>The schema defines a non-negative number <code>i</code>, a string <code>s</code>, a non-empty array of <code>t</code>, and <code>ip</code> / <code>port</code>. Only <code>i</code> is required.</p> <p>At the same time, we need to implement the <strong>check_schema(conf)</strong> method to complete the specification verification.</p> -<pre><code class="hljs css language-lua"> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.check_schema</span><span class="hljs-params">(conf)</span></span> - <span class="hljs-keyword">return</span> core.schema.check(schema, conf) - <span class="hljs-keyword">end</span> +<pre><code class="hljs css language-lua"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.check_schema</span><span class="hljs-params">(conf, schema_type)</span></span> + <span class="hljs-keyword">return</span> core.schema.check(schema, conf) +<span class="hljs-keyword">end</span> </code></pre> <p>Note: the project has provided the public method "<strong>core.schema.check</strong>", which can be used directly to complete JSON verification.</p> @@ -136,13 +145,52 @@ verification.</p> metadata_schema = metadata_schema, } </code></pre> +<p>You might have noticed the key-auth plugin has <code>type = 'auth'</code> in its definition. +When we set the type of plugin to <code>auth</code>, it means that this plugin is an authentication plugin.</p> +<p>An authentication plugin needs to choose a consumer after execution. For example, in key-auth plugin, it calls the <code>consumer.attach_consumer</code> to attach a consumer, which is chosen via the <code>apikey</code> header.</p> +<p>To interact with the <code>consumer</code> resource, this type of plugin needs to provide a <code>consumer_schema</code> to check the <code>plugins</code> configuration in the <code>consumer</code>.</p> +<p>Here is the consumer configuration for key-auth plugin:</p> +<pre><code class="hljs css language-json">{ + <span class="hljs-attr">"username"</span>: <span class="hljs-string">"Joe"</span>, + <span class="hljs-attr">"plugins"</span>: { + <span class="hljs-attr">"key-auth"</span>: { + <span class="hljs-attr">"key"</span>: <span class="hljs-string">"Joe's key"</span> + } + } +} +</code></pre> +<p>It will be used when you try to create a <a href="https://github.com/apache/apisix/blob/master/doc/admin-api.md#consumer">Consumer</a></p> +<p>To validate the configuration, the plugin uses a schema like this:</p> +<pre><code class="hljs css language-json">local consumer_schema = { + type = "object", + additionalProperties = false, + properties = { + key = {type = "string"}, + }, + required = {"key"}, +} +</code></pre> +<p>Note the difference between key-auth's <strong>check_schema(conf)</strong> method to example-plugin's:</p> +<pre><code class="hljs css language-lua"><span class="hljs-comment">-- key-auth</span> +<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.check_schema</span><span class="hljs-params">(conf, schema_type)</span></span> + <span class="hljs-keyword">if</span> schema_type == core.schema.TYPE_CONSUMER <span class="hljs-keyword">then</span> + <span class="hljs-keyword">return</span> core.schema.check(consumer_schema, conf) + <span class="hljs-keyword">else</span> + <span class="hljs-keyword">return</span> core.schema.check(schema, conf) + <span class="hljs-keyword">end</span> +<span class="hljs-keyword">end</span> +</code></pre> +<pre><code class="hljs css language-lua"><span class="hljs-comment">-- example-plugin</span> +<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.check_schema</span><span class="hljs-params">(conf, schema_type)</span></span> + <span class="hljs-keyword">return</span> core.schema.check(schema, conf) +<span class="hljs-keyword">end</span> +</code></pre> <h2><a class="anchor" aria-hidden="true" id="choose-phase-to-run"></a><a href="#choose-phase-to-run" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.2 [...] <p>Determine which phase to run, generally access or rewrite. If you don't know the <a href="https://openresty-reference.readthedocs.io/en/latest/Directives/">Openresty life cycle</a>, it's recommended to know it in advance. For example key-auth is an authentication plugin, thus the authentication should be completed -before forwarding the request to any upstream service. Therefore, the plugin can be executed in the rewrite and access phases. -In APISIX, the authentication logic is implemented in the rewrite phase. Generally, IP access and interface -permission are completed in the access phase.</p> -<p>The following code snippet shows how to implement any logic relevant to the plugin in the Openresty log phase.</p> +before forwarding the request to any upstream service. Therefore, the plugin must be executed in the rewrite phases. +In APISIX, only the authentication logic can be run in the rewrite phase. Other logic needs to run before proxy should be in access phase.</p> +<p>The following code snippet shows how to implement any logic relevant to the plugin in the OpenResty log phase.</p> <pre><code class="hljs css language-lua"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.log</span><span class="hljs-params">(conf)</span></span> <span class="hljs-comment">-- Implement logic here</span> <span class="hljs-keyword">end</span> @@ -161,7 +209,7 @@ of the real test case. For example, the key-auth plugin :</p> location /t { content_by_lua_block { <span class="hljs-keyword">local</span> plugin = <span class="hljs-keyword">require</span>(<span class="hljs-string">"apisix.plugins.key-auth"</span>) - <span class="hljs-keyword">local</span> ok, err = plugin.check_schema({key = <span class="hljs-string">'test-key'</span>}) + <span class="hljs-keyword">local</span> ok, err = plugin.check_schema({key = <span class="hljs-string">'test-key'</span>}, core.schema.TYPE_CONSUMER) <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> ok then ngx.say(err) end @@ -192,7 +240,49 @@ are located in the following folder: 't/servroot/logs'.</p> framework will assemble into a complete nginx.conf file. "<strong>t/servroot</strong>" is the working directory of Nginx and start the Nginx instance. according to the information provided by the test case, initiate the http request and check that the return items of HTTP include HTTP status, HTTP response header, HTTP response body and so on.</p> -</span></div></article></div><div class="docs-prevnext"></div></div></div><nav class="onPageNav"><ul class="toc-headings"><li><a href="#check-dependencies">check dependencies</a></li><li><a href="#name-and-config">name and config</a></li><li><a href="#schema-and-check">schema and check</a></li><li><a href="#choose-phase-to-run">choose phase to run</a></li><li><a href="#implement-the-logic">implement the logic</a></li><li><a href="#write-test-case">write test case</a><ul class="toc-headin [...] +<h3><a class="anchor" aria-hidden="true" id="register-public-api"></a><a href="#register-public-api" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.2 [...] +<p>A plugin can register API which exposes to the public. Take jwt-auth plugin as an example, this plugin registers <code>GET /apisix/plugin/jwt/sign</code> to allow client to sign its key:</p> +<pre><code class="hljs css language-lua"><span class="hljs-keyword">local</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">gen_token</span><span class="hljs-params">()</span></span> + ... +<span class="hljs-keyword">end</span> + +<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.api</span><span class="hljs-params">()</span></span> + <span class="hljs-keyword">return</span> { + { + methods = {<span class="hljs-string">"GET"</span>}, + uri = <span class="hljs-string">"/apisix/plugin/jwt/sign"</span>, + handler = gen_token, + } + } +<span class="hljs-keyword">end</span> +</code></pre> +<p>Note that the public API is exposed to the public. +You may need to use <a href="/apisix/plugin-interceptors">interceptors</a> to protect it.</p> +<h3><a class="anchor" aria-hidden="true" id="register-control-api"></a><a href="#register-control-api" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1 [...] +<p>If you only want to expose the API to the localhost or intranet, you can expose it via <a href="/apisix/control-api">Control API</a>.</p> +<p>Take a look at example-plugin plugin:</p> +<pre><code class="hljs css language-lua"><span class="hljs-keyword">local</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hello</span><span class="hljs-params">()</span></span> + <span class="hljs-keyword">local</span> args = ngx.req.get_uri_args() + <span class="hljs-keyword">if</span> args[<span class="hljs-string">"json"</span>] <span class="hljs-keyword">then</span> + <span class="hljs-keyword">return</span> <span class="hljs-number">200</span>, {msg = <span class="hljs-string">"world"</span>} + <span class="hljs-keyword">else</span> + <span class="hljs-keyword">return</span> <span class="hljs-number">200</span>, <span class="hljs-string">"world\n"</span> + <span class="hljs-keyword">end</span> +<span class="hljs-keyword">end</span> + + +<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.control_api</span><span class="hljs-params">()</span></span> + <span class="hljs-keyword">return</span> { + { + methods = {<span class="hljs-string">"GET"</span>}, + uris = {<span class="hljs-string">"/v1/plugin/example-plugin/hello"</span>}, + handler = hello, + } + } +<span class="hljs-keyword">end</span> +</code></pre> +<p>If you don't change the default control API configuration, the plugin will be expose <code>GET /v1/plugin/example-plugin/hello</code> which can only be accessed via <code>127.0.0.1</code>.</p> +</span></div></article></div><div class="docs-prevnext"></div></div></div><nav class="onPageNav"><ul class="toc-headings"><li><a href="#check-dependencies">check dependencies</a></li><li><a href="#name-and-config">name and config</a></li><li><a href="#schema-and-check">schema and check</a></li><li><a href="#choose-phase-to-run">choose phase to run</a></li><li><a href="#implement-the-logic">implement the logic</a></li><li><a href="#write-test-case">write test case</a><ul class="toc-headin [...] document.addEventListener('keyup', function(e) { if (e.target !== document.body) { return; diff --git a/apisix/zh-cn/install-dependencies.html b/apisix/zh-cn/install-dependencies.html index 42d4d27..d517b63 100644 --- a/apisix/zh-cn/install-dependencies.html +++ b/apisix/zh-cn/install-dependencies.html @@ -109,9 +109,9 @@ sudo add-apt-repository -y "deb http://openresty.org/package/debian $(lsb_releas sudo apt-get update <span class="hljs-meta"> #</span><span class="bash"> 安装 etcd</span> -wget https://github.com/etcd-io/etcd/releases/download/v3.3.13/etcd-v3.3.13-linux-amd64.tar.gz -tar -xvf etcd-v3.3.13-linux-amd64.tar.gz && \ - cd etcd-v3.3.13-linux-amd64 && \ +wget https://github.com/etcd-io/etcd/releases/download/v3.4.13/etcd-v3.4.13-linux-amd64.tar.gz +tar -xvf etcd-v3.4.13-linux-amd64.tar.gz && \ + cd etcd-v3.4.13-linux-amd64 && \ sudo cp -a etcd etcdctl /usr/bin/ <span class="hljs-meta"> #</span><span class="bash"> 安装 OpenResty 和 编译工具</span> diff --git a/apisix/zh-cn/install-dependencies/index.html b/apisix/zh-cn/install-dependencies/index.html index 42d4d27..d517b63 100644 --- a/apisix/zh-cn/install-dependencies/index.html +++ b/apisix/zh-cn/install-dependencies/index.html @@ -109,9 +109,9 @@ sudo add-apt-repository -y "deb http://openresty.org/package/debian $(lsb_releas sudo apt-get update <span class="hljs-meta"> #</span><span class="bash"> 安装 etcd</span> -wget https://github.com/etcd-io/etcd/releases/download/v3.3.13/etcd-v3.3.13-linux-amd64.tar.gz -tar -xvf etcd-v3.3.13-linux-amd64.tar.gz && \ - cd etcd-v3.3.13-linux-amd64 && \ +wget https://github.com/etcd-io/etcd/releases/download/v3.4.13/etcd-v3.4.13-linux-amd64.tar.gz +tar -xvf etcd-v3.4.13-linux-amd64.tar.gz && \ + cd etcd-v3.4.13-linux-amd64 && \ sudo cp -a etcd etcdctl /usr/bin/ <span class="hljs-meta"> #</span><span class="bash"> 安装 OpenResty 和 编译工具</span> diff --git a/apisix/zh-cn/plugin-develop.html b/apisix/zh-cn/plugin-develop.html index 06e3f92..f8744db 100644 --- a/apisix/zh-cn/plugin-develop.html +++ b/apisix/zh-cn/plugin-develop.html @@ -31,6 +31,8 @@ <li><a href="#确定执行阶段"><strong>确定执行阶段</strong></a></li> <li><a href="#编写执行逻辑"><strong>编写执行逻辑</strong></a></li> <li><a href="#编写测试用例"><strong>编写测试用例</strong></a></li> +<li><a href="#注册公共接口"><strong>注册公共接口</strong></a></li> +<li><a href="#注册控制接口"><strong>注册控制接口</strong></a></li> </ul> <h2><a class="anchor" aria-hidden="true" id="检查外部依赖"></a><a href="#检查外部依赖" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2. [...] <p>如果你的插件,涉及到一些外部的依赖和三方库,请首先检查一下依赖项的内容。 如果插件需要用到共享内存,需要在 <strong>bin/apisix</strong> 文 @@ -52,22 +54,21 @@ 可能需要在 <strong>bin/apisix</strong> 文件中,对 Nginx 配置文件生成的部分,添加一些你需要的处理。但是这样容易对全局产生影响,根据现有的 插件机制,我们不建议这样做,除非你已经对代码完全掌握。</p> <h2><a class="anchor" aria-hidden="true" id="插件命名与配置"></a><a href="#插件命名与配置" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1- [...] -<p>给插件取一个很棒的名字,确定插件的加载优先级,然后在 <strong>conf/config-default.yaml</strong> 文件中添加上你的插件名。例如 key-auth 这个插件, -需要在代码里指定插件名称(名称是插件的唯一标识,不可重名),在 <strong>apisix/plugins/key-auth.lua</strong> 文件中可以看到:</p> -<pre><code class="hljs css language-lua"> <span class="hljs-keyword">local</span> plugin_name = <span class="hljs-string">"key-auth"</span> +<p>给插件取一个很棒的名字,确定插件的加载优先级,然后在 <strong>conf/config-default.yaml</strong> 文件中添加上你的插件名。例如 example-plugin 这个插件, +需要在代码里指定插件名称(名称是插件的唯一标识,不可重名),在 <strong>apisix/plugins/example-plugin.lua</strong> 文件中可以看到:</p> +<pre><code class="hljs css language-lua"><span class="hljs-keyword">local</span> plugin_name = <span class="hljs-string">"example-plugin"</span> - <span class="hljs-keyword">local</span> _M = { - version = <span class="hljs-number">0.1</span>, - priority = <span class="hljs-number">2500</span>, - <span class="hljs-built_in">type</span> = <span class="hljs-string">'auth'</span>, - name = plugin_name, - schema = schema, - } +<span class="hljs-keyword">local</span> _M = { + version = <span class="hljs-number">0.1</span>, + priority = <span class="hljs-number">0</span>, + name = plugin_name, + schema = schema, + metadata_schema = metadata_schema, +} </code></pre> -<p>注:新插件的优先级( priority 属性 )不能与现有插件的优先级相同。另外,优先级( priority )值大的插件,会优先执行,比如 <code>basic-auth</code> 的优先级是 2520 ,<code>ip-restriction</code> 的优先级是 3000 ,所以在每个阶段,会先执行 <code>ip-restriction</code> 插件,再去执行 <code>basic-auth</code> 插件。</p> +<p>注:新插件的优先级( priority 属性 )不能与现有插件的优先级相同。另外,同一个阶段里面,优先级( priority )值大的插件,会优先执行,比如 <code>example-plugin</code> 的优先级是 0 ,<code>ip-restriction</code> 的优先级是 3000 ,所以在每个阶段,会先执行 <code>ip-restriction</code> 插件,再去执行 <code>example-plugin</code> 插件。这里的“阶段”的定义,参见后续的<a href="#确定执行阶段">确定执行阶段</a>这一节。</p> <p>在 <strong>conf/config-default.yaml</strong> 配置文件中,列出了启用的插件(都是以插件名指定的):</p> <pre><code class="hljs css language-yaml"><span class="hljs-attr">plugins:</span> <span class="hljs-comment"># plugin list</span> - <span class="hljs-bullet">-</span> <span class="hljs-string">example-plugin</span> <span class="hljs-bullet">-</span> <span class="hljs-string">limit-req</span> <span class="hljs-bullet">-</span> <span class="hljs-string">limit-count</span> <span class="hljs-bullet">-</span> <span class="hljs-string">limit-conn</span> @@ -83,6 +84,7 @@ <span class="hljs-bullet">-</span> <span class="hljs-string">openid-connect</span> <span class="hljs-bullet">-</span> <span class="hljs-string">proxy-rewrite</span> <span class="hljs-bullet">-</span> <span class="hljs-string">redirect</span> + <span class="hljs-string">...</span> </code></pre> <p>注:先后顺序与执行顺序无关。</p> <p>特别需要注意的是,如果你的插件有新建自己的代码目录,那么就需要修改 Makefile 文件,新增创建文件夹的操作,比如:</p> @@ -91,23 +93,31 @@ </code></pre> <h2><a class="anchor" aria-hidden="true" id="配置描述与校验"></a><a href="#配置描述与校验" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1- [...] <p>定义插件的配置项,以及对应的 <a href="https://json-schema.org">Json Schema</a> 描述,并完成对 json 的校验,这样方便对配置的数据规 -格进行验证,以确保数据的完整性以及程序的健壮性。同样,我们以 key-auth 插件为例,看看他的配置数据:</p> -<pre><code class="hljs css language-json"> "key-auth" : { - "key" : "auth-one" - } +格进行验证,以确保数据的完整性以及程序的健壮性。同样,我们以 example-plugin 插件为例,看看他的配置数据:</p> +<pre><code class="hljs css language-json">"example-plugin" : { + "i": 1, + "s": "s", + "t": [1] +} </code></pre> -<p>插件的配置数据比较简单,只支持一个命名为 key 的属性,那么我们看下他的 Schema 描述:</p> -<pre><code class="hljs css language-lua"> <span class="hljs-keyword">local</span> schema = { - <span class="hljs-built_in">type</span> = <span class="hljs-string">"object"</span>, - properties = { - key = {<span class="hljs-built_in">type</span> = <span class="hljs-string">"string"</span>}, - } - } +<p>我们看下他的 Schema 描述:</p> +<pre><code class="hljs css language-lua"><span class="hljs-keyword">local</span> schema = { + <span class="hljs-built_in">type</span> = <span class="hljs-string">"object"</span>, + properties = { + i = {<span class="hljs-built_in">type</span> = <span class="hljs-string">"number"</span>, minimum = <span class="hljs-number">0</span>}, + s = {<span class="hljs-built_in">type</span> = <span class="hljs-string">"string"</span>}, + t = {<span class="hljs-built_in">type</span> = <span class="hljs-string">"array"</span>, minItems = <span class="hljs-number">1</span>}, + ip = {<span class="hljs-built_in">type</span> = <span class="hljs-string">"string"</span>}, + port = {<span class="hljs-built_in">type</span> = <span class="hljs-string">"integer"</span>}, + }, + required = {<span class="hljs-string">"i"</span>}, +} </code></pre> +<p>这个 schema 定义了一个非负数 <code>i</code>,字符串 <code>s</code>,非空数组 <code>t</code>,和 <code>ip</code> 跟 <code>port</code>。只有 <code>i</code> 是必需的。</p> <p>同时,需要实现 <strong>check_schema(conf)</strong> 方法,完成配置参数的合法性校验。</p> -<pre><code class="hljs css language-lua"> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.check_schema</span><span class="hljs-params">(conf)</span></span> - <span class="hljs-keyword">return</span> core.schema.check(schema, conf) - <span class="hljs-keyword">end</span> +<pre><code class="hljs css language-lua"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.check_schema</span><span class="hljs-params">(conf)</span></span> + <span class="hljs-keyword">return</span> core.schema.check(schema, conf) +<span class="hljs-keyword">end</span> </code></pre> <p>注:项目已经提供了 <strong>core.schema.check</strong> 公共方法,直接使用即可完成配置参数校验。</p> <p>另外,如果插件需要使用一些元数据,可以定义插件的 <code>metadata_schema</code> ,然后就可以通过 <code>admin api</code> 动态的管理这些元数据了。如:</p> @@ -131,10 +141,48 @@ metadata_schema = metadata_schema, } </code></pre> +<p>你可能之前见过 key-auth 这个插件在它的模块定义时设置了 <code>type = 'auth'</code>。 +当一个插件设置 <code>type = 'auth'</code>,说明它是个认证插件。</p> +<p>认证插件需要在执行后选择对应的consumer。举个例子,在 key-auth 插件中,它通过 <code>apikey</code> 请求头获取对应的 consumer,然后通过 <code>consumer.attach_consumer</code> 设置它。</p> +<p>为了跟 <code>consumer</code> 资源一起使用,认证插件需要提供一个 <code>consumer_schema</code> 来检验 <code>consumer</code> 资源的 <code>plugins</code> 属性里面的配置。</p> +<p>下面是 key-auth 插件的 consumer 配置:</p> +<pre><code class="hljs css language-json">{ + <span class="hljs-attr">"username"</span>: <span class="hljs-string">"Joe"</span>, + <span class="hljs-attr">"plugins"</span>: { + <span class="hljs-attr">"key-auth"</span>: { + <span class="hljs-attr">"key"</span>: <span class="hljs-string">"Joe's key"</span> + } + } +} +</code></pre> +<p>你在创建 <a href="https://github.com/apache/apisix/blob/master/doc/admin-api.md#consumer">Consumer</a> 时会用到它。</p> +<p>为了检验这个配置,这个插件使用了如下的schema:</p> +<pre><code class="hljs css language-json">local consumer_schema = { + type = "object", + additionalProperties = false, + properties = { + key = {type = "string"}, + }, + required = {"key"}, +} +</code></pre> +<p>注意 key-auth 的 <strong>check_schema(conf)</strong> 方法和 example-plugin 的同名方法的区别:</p> +<pre><code class="hljs css language-lua"><span class="hljs-comment">-- key-auth</span> +<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.check_schema</span><span class="hljs-params">(conf, schema_type)</span></span> + <span class="hljs-keyword">if</span> schema_type == core.schema.TYPE_CONSUMER <span class="hljs-keyword">then</span> + <span class="hljs-keyword">return</span> core.schema.check(consumer_schema, conf) + <span class="hljs-keyword">else</span> + <span class="hljs-keyword">return</span> core.schema.check(schema, conf) + <span class="hljs-keyword">end</span> +<span class="hljs-keyword">end</span> +</code></pre> +<pre><code class="hljs css language-lua"><span class="hljs-comment">-- example-plugin</span> +<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.check_schema</span><span class="hljs-params">(conf, schema_type)</span></span> + <span class="hljs-keyword">return</span> core.schema.check(schema, conf) +<span class="hljs-keyword">end</span> +</code></pre> <h2><a class="anchor" aria-hidden="true" id="确定执行阶段"></a><a href="#确定执行阶段" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2. [...] -<p>根据业务功能,确定你的插件需要在哪个阶段执行。 key-auth 是一个认证插件,只要在请求进来之后业务响应之前完成认证即可。 -该插件在 rewrite 、access 阶段执行都可以,项目中是用 rewrite 阶段执行认证逻辑,一般 IP 准入、接口权限是在 access 阶段 -完成的。</p> +<p>根据业务功能,确定你的插件需要在哪个阶段执行。 key-auth 是一个认证插件,所以需要在 rewrite 阶段执行。在 APISIX,只有认证逻辑可以在 rewrite 阶段里面完成,其他需要在代理到上游之前执行的逻辑都是在 access 阶段完成的。</p> <p><strong>注意:我们不能在 rewrite 和 access 阶段调用 <code>ngx.exit</code> 或者 <code>core.respond.exit</code>。如果确实需要退出,只需要 return 状态码和正文,插件引擎将使用返回的状态码和正文进行退出。<a href="https://github.com/apache/apisix/blob/35269581e21473e1a27b11cceca6f773cad0192a/apisix/plugins/limit-count.lua#L177">例子</a></strong></p> <h2><a class="anchor" aria-hidden="true" id="编写执行逻辑"></a><a href="#编写执行逻辑" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2. [...] <p>在对应的阶段方法里编写功能的逻辑代码。</p> @@ -148,7 +196,7 @@ location /t { content_by_lua_block { <span class="hljs-keyword">local</span> plugin = <span class="hljs-keyword">require</span>(<span class="hljs-string">"apisix.plugins.key-auth"</span>) - <span class="hljs-keyword">local</span> ok, err = plugin.check_schema({key = <span class="hljs-string">'test-key'</span>}) + <span class="hljs-keyword">local</span> ok, err = plugin.check_schema({key = <span class="hljs-string">'test-key'</span>}, core.schema.TYPE_CONSUMER) <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> ok then ngx.say(err) end @@ -176,7 +224,49 @@ done <p>根据我们在 Makefile 里配置的 PATH,和每一个 <strong>.t</strong> 文件最前面的一些配置项,框架会组装成一个完整的 nginx.conf 文件, <strong>t/servroot</strong> 会被当成 Nginx 的工作目录,启动 Nginx 实例。根据测试用例提供的信息,发起 http 请求并检查 http 的返回项, 包括 http status,http response header, http response body 等。</p> -</span></div></article></div><div class="docs-prevnext"></div></div></div><nav class="onPageNav"><ul class="toc-headings"><li><a href="#检查外部依赖">检查外部依赖</a></li><li><a href="#插件命名与配置">插件命名与配置</a></li><li><a href="#配置描述与校验">配置描述与校验</a></li><li><a href="#确定执行阶段">确定执行阶段</a></li><li><a href="#编写执行逻辑">编写执行逻辑</a></li><li><a href="#编写测试用例">编写测试用例</a><ul class="toc-headings"><li><a href="#附上test-nginx-执行流程">附上test-nginx 执行流程</a></li></ul></li></ul></nav></div><footer class="nav-footer" id="footer" [...] +<h3><a class="anchor" aria-hidden="true" id="注册公共接口"></a><a href="#注册公共接口" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2. [...] +<p>插件可以注册暴露给公网的接口。以 jwt-auth 插件为例,这个插件为了让客户端能够签名,注册了 <code>GET /apisix/plugin/jwt/sign</code> 这个接口:</p> +<pre><code class="hljs css language-lua"><span class="hljs-keyword">local</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">gen_token</span><span class="hljs-params">()</span></span> + ... +<span class="hljs-keyword">end</span> + +<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.api</span><span class="hljs-params">()</span></span> + <span class="hljs-keyword">return</span> { + { + methods = {<span class="hljs-string">"GET"</span>}, + uri = <span class="hljs-string">"/apisix/plugin/jwt/sign"</span>, + handler = gen_token, + } + } +<span class="hljs-keyword">end</span> +</code></pre> +<p>注意注册的接口会暴露到外网。 +你可能需要使用 <a href="/apisix/zh-cn/plugin-interceptors">interceptors</a> 来保护它。</p> +<h3><a class="anchor" aria-hidden="true" id="注册控制接口"></a><a href="#注册控制接口" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2. [...] +<p>如果你只想暴露 API 到 localhost 或内网,你可以通过 <a href="./control-api.md">Control API</a> 来暴露它。</p> +<p>Take a look at example-plugin plugin:</p> +<pre><code class="hljs css language-lua"><span class="hljs-keyword">local</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hello</span><span class="hljs-params">()</span></span> + <span class="hljs-keyword">local</span> args = ngx.req.get_uri_args() + <span class="hljs-keyword">if</span> args[<span class="hljs-string">"json"</span>] <span class="hljs-keyword">then</span> + <span class="hljs-keyword">return</span> <span class="hljs-number">200</span>, {msg = <span class="hljs-string">"world"</span>} + <span class="hljs-keyword">else</span> + <span class="hljs-keyword">return</span> <span class="hljs-number">200</span>, <span class="hljs-string">"world\n"</span> + <span class="hljs-keyword">end</span> +<span class="hljs-keyword">end</span> + + +<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.control_api</span><span class="hljs-params">()</span></span> + <span class="hljs-keyword">return</span> { + { + methods = {<span class="hljs-string">"GET"</span>}, + uris = {<span class="hljs-string">"/v1/plugin/example-plugin/hello"</span>}, + handler = hello, + } + } +<span class="hljs-keyword">end</span> +</code></pre> +<p>如果你没有改过默认的 control API 配置,这个插件暴露的 <code>GET /v1/plugin/example-plugin/hello</code> API 只有通过 <code>127.0.0.1</code> 才能访问它。</p> +</span></div></article></div><div class="docs-prevnext"></div></div></div><nav class="onPageNav"><ul class="toc-headings"><li><a href="#检查外部依赖">检查外部依赖</a></li><li><a href="#插件命名与配置">插件命名与配置</a></li><li><a href="#配置描述与校验">配置描述与校验</a></li><li><a href="#确定执行阶段">确定执行阶段</a></li><li><a href="#编写执行逻辑">编写执行逻辑</a></li><li><a href="#编写测试用例">编写测试用例</a><ul class="toc-headings"><li><a href="#附上test-nginx-执行流程">附上test-nginx 执行流程</a></li><li><a href="#注册公共接口">注册公共接口</a></li><li><a href="#注册控制接口">注册控制接口 [...] document.addEventListener('keyup', function(e) { if (e.target !== document.body) { return; diff --git a/apisix/zh-cn/plugin-develop/index.html b/apisix/zh-cn/plugin-develop/index.html index 06e3f92..f8744db 100644 --- a/apisix/zh-cn/plugin-develop/index.html +++ b/apisix/zh-cn/plugin-develop/index.html @@ -31,6 +31,8 @@ <li><a href="#确定执行阶段"><strong>确定执行阶段</strong></a></li> <li><a href="#编写执行逻辑"><strong>编写执行逻辑</strong></a></li> <li><a href="#编写测试用例"><strong>编写测试用例</strong></a></li> +<li><a href="#注册公共接口"><strong>注册公共接口</strong></a></li> +<li><a href="#注册控制接口"><strong>注册控制接口</strong></a></li> </ul> <h2><a class="anchor" aria-hidden="true" id="检查外部依赖"></a><a href="#检查外部依赖" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2. [...] <p>如果你的插件,涉及到一些外部的依赖和三方库,请首先检查一下依赖项的内容。 如果插件需要用到共享内存,需要在 <strong>bin/apisix</strong> 文 @@ -52,22 +54,21 @@ 可能需要在 <strong>bin/apisix</strong> 文件中,对 Nginx 配置文件生成的部分,添加一些你需要的处理。但是这样容易对全局产生影响,根据现有的 插件机制,我们不建议这样做,除非你已经对代码完全掌握。</p> <h2><a class="anchor" aria-hidden="true" id="插件命名与配置"></a><a href="#插件命名与配置" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1- [...] -<p>给插件取一个很棒的名字,确定插件的加载优先级,然后在 <strong>conf/config-default.yaml</strong> 文件中添加上你的插件名。例如 key-auth 这个插件, -需要在代码里指定插件名称(名称是插件的唯一标识,不可重名),在 <strong>apisix/plugins/key-auth.lua</strong> 文件中可以看到:</p> -<pre><code class="hljs css language-lua"> <span class="hljs-keyword">local</span> plugin_name = <span class="hljs-string">"key-auth"</span> +<p>给插件取一个很棒的名字,确定插件的加载优先级,然后在 <strong>conf/config-default.yaml</strong> 文件中添加上你的插件名。例如 example-plugin 这个插件, +需要在代码里指定插件名称(名称是插件的唯一标识,不可重名),在 <strong>apisix/plugins/example-plugin.lua</strong> 文件中可以看到:</p> +<pre><code class="hljs css language-lua"><span class="hljs-keyword">local</span> plugin_name = <span class="hljs-string">"example-plugin"</span> - <span class="hljs-keyword">local</span> _M = { - version = <span class="hljs-number">0.1</span>, - priority = <span class="hljs-number">2500</span>, - <span class="hljs-built_in">type</span> = <span class="hljs-string">'auth'</span>, - name = plugin_name, - schema = schema, - } +<span class="hljs-keyword">local</span> _M = { + version = <span class="hljs-number">0.1</span>, + priority = <span class="hljs-number">0</span>, + name = plugin_name, + schema = schema, + metadata_schema = metadata_schema, +} </code></pre> -<p>注:新插件的优先级( priority 属性 )不能与现有插件的优先级相同。另外,优先级( priority )值大的插件,会优先执行,比如 <code>basic-auth</code> 的优先级是 2520 ,<code>ip-restriction</code> 的优先级是 3000 ,所以在每个阶段,会先执行 <code>ip-restriction</code> 插件,再去执行 <code>basic-auth</code> 插件。</p> +<p>注:新插件的优先级( priority 属性 )不能与现有插件的优先级相同。另外,同一个阶段里面,优先级( priority )值大的插件,会优先执行,比如 <code>example-plugin</code> 的优先级是 0 ,<code>ip-restriction</code> 的优先级是 3000 ,所以在每个阶段,会先执行 <code>ip-restriction</code> 插件,再去执行 <code>example-plugin</code> 插件。这里的“阶段”的定义,参见后续的<a href="#确定执行阶段">确定执行阶段</a>这一节。</p> <p>在 <strong>conf/config-default.yaml</strong> 配置文件中,列出了启用的插件(都是以插件名指定的):</p> <pre><code class="hljs css language-yaml"><span class="hljs-attr">plugins:</span> <span class="hljs-comment"># plugin list</span> - <span class="hljs-bullet">-</span> <span class="hljs-string">example-plugin</span> <span class="hljs-bullet">-</span> <span class="hljs-string">limit-req</span> <span class="hljs-bullet">-</span> <span class="hljs-string">limit-count</span> <span class="hljs-bullet">-</span> <span class="hljs-string">limit-conn</span> @@ -83,6 +84,7 @@ <span class="hljs-bullet">-</span> <span class="hljs-string">openid-connect</span> <span class="hljs-bullet">-</span> <span class="hljs-string">proxy-rewrite</span> <span class="hljs-bullet">-</span> <span class="hljs-string">redirect</span> + <span class="hljs-string">...</span> </code></pre> <p>注:先后顺序与执行顺序无关。</p> <p>特别需要注意的是,如果你的插件有新建自己的代码目录,那么就需要修改 Makefile 文件,新增创建文件夹的操作,比如:</p> @@ -91,23 +93,31 @@ </code></pre> <h2><a class="anchor" aria-hidden="true" id="配置描述与校验"></a><a href="#配置描述与校验" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1- [...] <p>定义插件的配置项,以及对应的 <a href="https://json-schema.org">Json Schema</a> 描述,并完成对 json 的校验,这样方便对配置的数据规 -格进行验证,以确保数据的完整性以及程序的健壮性。同样,我们以 key-auth 插件为例,看看他的配置数据:</p> -<pre><code class="hljs css language-json"> "key-auth" : { - "key" : "auth-one" - } +格进行验证,以确保数据的完整性以及程序的健壮性。同样,我们以 example-plugin 插件为例,看看他的配置数据:</p> +<pre><code class="hljs css language-json">"example-plugin" : { + "i": 1, + "s": "s", + "t": [1] +} </code></pre> -<p>插件的配置数据比较简单,只支持一个命名为 key 的属性,那么我们看下他的 Schema 描述:</p> -<pre><code class="hljs css language-lua"> <span class="hljs-keyword">local</span> schema = { - <span class="hljs-built_in">type</span> = <span class="hljs-string">"object"</span>, - properties = { - key = {<span class="hljs-built_in">type</span> = <span class="hljs-string">"string"</span>}, - } - } +<p>我们看下他的 Schema 描述:</p> +<pre><code class="hljs css language-lua"><span class="hljs-keyword">local</span> schema = { + <span class="hljs-built_in">type</span> = <span class="hljs-string">"object"</span>, + properties = { + i = {<span class="hljs-built_in">type</span> = <span class="hljs-string">"number"</span>, minimum = <span class="hljs-number">0</span>}, + s = {<span class="hljs-built_in">type</span> = <span class="hljs-string">"string"</span>}, + t = {<span class="hljs-built_in">type</span> = <span class="hljs-string">"array"</span>, minItems = <span class="hljs-number">1</span>}, + ip = {<span class="hljs-built_in">type</span> = <span class="hljs-string">"string"</span>}, + port = {<span class="hljs-built_in">type</span> = <span class="hljs-string">"integer"</span>}, + }, + required = {<span class="hljs-string">"i"</span>}, +} </code></pre> +<p>这个 schema 定义了一个非负数 <code>i</code>,字符串 <code>s</code>,非空数组 <code>t</code>,和 <code>ip</code> 跟 <code>port</code>。只有 <code>i</code> 是必需的。</p> <p>同时,需要实现 <strong>check_schema(conf)</strong> 方法,完成配置参数的合法性校验。</p> -<pre><code class="hljs css language-lua"> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.check_schema</span><span class="hljs-params">(conf)</span></span> - <span class="hljs-keyword">return</span> core.schema.check(schema, conf) - <span class="hljs-keyword">end</span> +<pre><code class="hljs css language-lua"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.check_schema</span><span class="hljs-params">(conf)</span></span> + <span class="hljs-keyword">return</span> core.schema.check(schema, conf) +<span class="hljs-keyword">end</span> </code></pre> <p>注:项目已经提供了 <strong>core.schema.check</strong> 公共方法,直接使用即可完成配置参数校验。</p> <p>另外,如果插件需要使用一些元数据,可以定义插件的 <code>metadata_schema</code> ,然后就可以通过 <code>admin api</code> 动态的管理这些元数据了。如:</p> @@ -131,10 +141,48 @@ metadata_schema = metadata_schema, } </code></pre> +<p>你可能之前见过 key-auth 这个插件在它的模块定义时设置了 <code>type = 'auth'</code>。 +当一个插件设置 <code>type = 'auth'</code>,说明它是个认证插件。</p> +<p>认证插件需要在执行后选择对应的consumer。举个例子,在 key-auth 插件中,它通过 <code>apikey</code> 请求头获取对应的 consumer,然后通过 <code>consumer.attach_consumer</code> 设置它。</p> +<p>为了跟 <code>consumer</code> 资源一起使用,认证插件需要提供一个 <code>consumer_schema</code> 来检验 <code>consumer</code> 资源的 <code>plugins</code> 属性里面的配置。</p> +<p>下面是 key-auth 插件的 consumer 配置:</p> +<pre><code class="hljs css language-json">{ + <span class="hljs-attr">"username"</span>: <span class="hljs-string">"Joe"</span>, + <span class="hljs-attr">"plugins"</span>: { + <span class="hljs-attr">"key-auth"</span>: { + <span class="hljs-attr">"key"</span>: <span class="hljs-string">"Joe's key"</span> + } + } +} +</code></pre> +<p>你在创建 <a href="https://github.com/apache/apisix/blob/master/doc/admin-api.md#consumer">Consumer</a> 时会用到它。</p> +<p>为了检验这个配置,这个插件使用了如下的schema:</p> +<pre><code class="hljs css language-json">local consumer_schema = { + type = "object", + additionalProperties = false, + properties = { + key = {type = "string"}, + }, + required = {"key"}, +} +</code></pre> +<p>注意 key-auth 的 <strong>check_schema(conf)</strong> 方法和 example-plugin 的同名方法的区别:</p> +<pre><code class="hljs css language-lua"><span class="hljs-comment">-- key-auth</span> +<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.check_schema</span><span class="hljs-params">(conf, schema_type)</span></span> + <span class="hljs-keyword">if</span> schema_type == core.schema.TYPE_CONSUMER <span class="hljs-keyword">then</span> + <span class="hljs-keyword">return</span> core.schema.check(consumer_schema, conf) + <span class="hljs-keyword">else</span> + <span class="hljs-keyword">return</span> core.schema.check(schema, conf) + <span class="hljs-keyword">end</span> +<span class="hljs-keyword">end</span> +</code></pre> +<pre><code class="hljs css language-lua"><span class="hljs-comment">-- example-plugin</span> +<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.check_schema</span><span class="hljs-params">(conf, schema_type)</span></span> + <span class="hljs-keyword">return</span> core.schema.check(schema, conf) +<span class="hljs-keyword">end</span> +</code></pre> <h2><a class="anchor" aria-hidden="true" id="确定执行阶段"></a><a href="#确定执行阶段" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2. [...] -<p>根据业务功能,确定你的插件需要在哪个阶段执行。 key-auth 是一个认证插件,只要在请求进来之后业务响应之前完成认证即可。 -该插件在 rewrite 、access 阶段执行都可以,项目中是用 rewrite 阶段执行认证逻辑,一般 IP 准入、接口权限是在 access 阶段 -完成的。</p> +<p>根据业务功能,确定你的插件需要在哪个阶段执行。 key-auth 是一个认证插件,所以需要在 rewrite 阶段执行。在 APISIX,只有认证逻辑可以在 rewrite 阶段里面完成,其他需要在代理到上游之前执行的逻辑都是在 access 阶段完成的。</p> <p><strong>注意:我们不能在 rewrite 和 access 阶段调用 <code>ngx.exit</code> 或者 <code>core.respond.exit</code>。如果确实需要退出,只需要 return 状态码和正文,插件引擎将使用返回的状态码和正文进行退出。<a href="https://github.com/apache/apisix/blob/35269581e21473e1a27b11cceca6f773cad0192a/apisix/plugins/limit-count.lua#L177">例子</a></strong></p> <h2><a class="anchor" aria-hidden="true" id="编写执行逻辑"></a><a href="#编写执行逻辑" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2. [...] <p>在对应的阶段方法里编写功能的逻辑代码。</p> @@ -148,7 +196,7 @@ location /t { content_by_lua_block { <span class="hljs-keyword">local</span> plugin = <span class="hljs-keyword">require</span>(<span class="hljs-string">"apisix.plugins.key-auth"</span>) - <span class="hljs-keyword">local</span> ok, err = plugin.check_schema({key = <span class="hljs-string">'test-key'</span>}) + <span class="hljs-keyword">local</span> ok, err = plugin.check_schema({key = <span class="hljs-string">'test-key'</span>}, core.schema.TYPE_CONSUMER) <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> ok then ngx.say(err) end @@ -176,7 +224,49 @@ done <p>根据我们在 Makefile 里配置的 PATH,和每一个 <strong>.t</strong> 文件最前面的一些配置项,框架会组装成一个完整的 nginx.conf 文件, <strong>t/servroot</strong> 会被当成 Nginx 的工作目录,启动 Nginx 实例。根据测试用例提供的信息,发起 http 请求并检查 http 的返回项, 包括 http status,http response header, http response body 等。</p> -</span></div></article></div><div class="docs-prevnext"></div></div></div><nav class="onPageNav"><ul class="toc-headings"><li><a href="#检查外部依赖">检查外部依赖</a></li><li><a href="#插件命名与配置">插件命名与配置</a></li><li><a href="#配置描述与校验">配置描述与校验</a></li><li><a href="#确定执行阶段">确定执行阶段</a></li><li><a href="#编写执行逻辑">编写执行逻辑</a></li><li><a href="#编写测试用例">编写测试用例</a><ul class="toc-headings"><li><a href="#附上test-nginx-执行流程">附上test-nginx 执行流程</a></li></ul></li></ul></nav></div><footer class="nav-footer" id="footer" [...] +<h3><a class="anchor" aria-hidden="true" id="注册公共接口"></a><a href="#注册公共接口" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2. [...] +<p>插件可以注册暴露给公网的接口。以 jwt-auth 插件为例,这个插件为了让客户端能够签名,注册了 <code>GET /apisix/plugin/jwt/sign</code> 这个接口:</p> +<pre><code class="hljs css language-lua"><span class="hljs-keyword">local</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">gen_token</span><span class="hljs-params">()</span></span> + ... +<span class="hljs-keyword">end</span> + +<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.api</span><span class="hljs-params">()</span></span> + <span class="hljs-keyword">return</span> { + { + methods = {<span class="hljs-string">"GET"</span>}, + uri = <span class="hljs-string">"/apisix/plugin/jwt/sign"</span>, + handler = gen_token, + } + } +<span class="hljs-keyword">end</span> +</code></pre> +<p>注意注册的接口会暴露到外网。 +你可能需要使用 <a href="/apisix/zh-cn/plugin-interceptors">interceptors</a> 来保护它。</p> +<h3><a class="anchor" aria-hidden="true" id="注册控制接口"></a><a href="#注册控制接口" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2. [...] +<p>如果你只想暴露 API 到 localhost 或内网,你可以通过 <a href="./control-api.md">Control API</a> 来暴露它。</p> +<p>Take a look at example-plugin plugin:</p> +<pre><code class="hljs css language-lua"><span class="hljs-keyword">local</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hello</span><span class="hljs-params">()</span></span> + <span class="hljs-keyword">local</span> args = ngx.req.get_uri_args() + <span class="hljs-keyword">if</span> args[<span class="hljs-string">"json"</span>] <span class="hljs-keyword">then</span> + <span class="hljs-keyword">return</span> <span class="hljs-number">200</span>, {msg = <span class="hljs-string">"world"</span>} + <span class="hljs-keyword">else</span> + <span class="hljs-keyword">return</span> <span class="hljs-number">200</span>, <span class="hljs-string">"world\n"</span> + <span class="hljs-keyword">end</span> +<span class="hljs-keyword">end</span> + + +<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_M.control_api</span><span class="hljs-params">()</span></span> + <span class="hljs-keyword">return</span> { + { + methods = {<span class="hljs-string">"GET"</span>}, + uris = {<span class="hljs-string">"/v1/plugin/example-plugin/hello"</span>}, + handler = hello, + } + } +<span class="hljs-keyword">end</span> +</code></pre> +<p>如果你没有改过默认的 control API 配置,这个插件暴露的 <code>GET /v1/plugin/example-plugin/hello</code> API 只有通过 <code>127.0.0.1</code> 才能访问它。</p> +</span></div></article></div><div class="docs-prevnext"></div></div></div><nav class="onPageNav"><ul class="toc-headings"><li><a href="#检查外部依赖">检查外部依赖</a></li><li><a href="#插件命名与配置">插件命名与配置</a></li><li><a href="#配置描述与校验">配置描述与校验</a></li><li><a href="#确定执行阶段">确定执行阶段</a></li><li><a href="#编写执行逻辑">编写执行逻辑</a></li><li><a href="#编写测试用例">编写测试用例</a><ul class="toc-headings"><li><a href="#附上test-nginx-执行流程">附上test-nginx 执行流程</a></li><li><a href="#注册公共接口">注册公共接口</a></li><li><a href="#注册控制接口">注册控制接口 [...] document.addEventListener('keyup', function(e) { if (e.target !== document.body) { return;