This is an automated email from the ASF dual-hosted git repository.

hsluoyz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/casbin-website.git


The following commit(s) were added to refs/heads/master by this push:
     new fb0f15e26 docs: refresh APISIX authz-casbin integration guide (#475)
fb0f15e26 is described below

commit fb0f15e26682516b0efe8b7258dcd257801e8dff
Author: Yilia Lin <[email protected]>
AuthorDate: Thu Aug 13 15:26:24 2026 +0800

    docs: refresh APISIX authz-casbin integration guide (#475)
---
 blog/2021-08-19-apisix-casbin-authorization.md    | 296 ++++++++++++++--------
 blog/authors.yml                                  |   6 +
 docs/Tutorial.mdx                                 |   4 +-
 src/tableData/MiddlewareData/MiddlewareLuaData.js |   4 +-
 4 files changed, 202 insertions(+), 108 deletions(-)

diff --git a/blog/2021-08-19-apisix-casbin-authorization.md 
b/blog/2021-08-19-apisix-casbin-authorization.md
index 6bb39c65d..2fee91561 100644
--- a/blog/2021-08-19-apisix-casbin-authorization.md
+++ b/blog/2021-08-19-apisix-casbin-authorization.md
@@ -1,29 +1,88 @@
 ---
 title: Authorization in APISIX Using Casbin
-authors: [rushitote]
+description: Enforce and update Casbin RBAC on the APISIX request path with 
reproducible allow, deny, and cleanup checks.
+authors: [rushitote, yilialin]
 ---
 
 ## Introduction
 
-[APISIX](https://apisix.apache.org/) is a high-performance, scalable, 
cloud-native API gateway built on Nginx and etcd, and an Apache Software 
Foundation project. It ships with many plugins for authentication, monitoring, 
routing, and more. Plugins are hot-reloaded without restarts, so you can change 
behavior on the fly.
+[Apache APISIX](https://apisix.apache.org/) can enforce Casbin authorization
+policies at the gateway with its built-in
+[`authz-casbin`](https://apisix.apache.org/docs/apisix/plugins/authz-casbin/)
+plugin. The plugin uses
+[Lua Casbin](https://github.com/apache/casbin-lua-casbin) and supports access
+control models such as ACL, RBAC, and ABAC.
 
-When you need **authorization** beyond simple checks, the **authz-casbin** 
plugin can help. It is an APISIX plugin built on [Lua 
Casbin](https://github.com/apache/casbin-lua-casbin/) that enforces flexible 
authorization using models such as ACL, RBAC, and ABAC. [Casbin](/) is an 
authorization library (originally in Go, now ported to many languages); Lua 
Casbin is the Lua port. We proposed the plugin in the APISIX repo 
([#4674](https://github.com/apache/apisix/issues/4674)); after review  [...]
+This guide puts a small RBAC policy on the APISIX request path. You will verify
+allowed and denied requests, then update a shared policy through plugin
+metadata without changing the route.
+
+The Casbin community originally proposed the integration in
+[APISIX Issue #4674](https://github.com/apache/apisix/issues/4674), and
+[APISIX PR #4710](https://github.com/apache/apisix/pull/4710) added the plugin.
+
+:::info August 2026 refresh
+
+This guide was refreshed in August 2026. The examples were validated with
+Apache APISIX 3.17.0. The original 2021 author remains credited, and Yilia Lin
+is added for the 2026 refresh.
+
+:::
 
 <!-- truncate -->
 
-This post shows how to implement **Role-Based Access Control (RBAC)** in 
APISIX using authz-casbin.
+:::warning Authentication boundary
+
+Casbin performs authorization, not authentication. The example uses a `user`
+request header as the subject only to keep the test small. In production, do
+not trust an identity header supplied directly by a client. An authentication
+layer or trusted proxy should establish the identity and remove or overwrite
+untrusted values before Casbin evaluates the request.
+
+:::
+
+## Prerequisites and starting state
+
+This guide starts with a disposable Apache APISIX 3.17.0 instance already
+running. If you need a local instance, follow the
+[APISIX Docker deployment guide](https://apisix.apache.org/docs/docker/manual/)
+and pin the APISIX image to `apache/apisix:3.17.0-debian`.
+
+The instance must have:
 
-**Note:** Casbin handles **authorization** only. Use another plugin or your 
own logic for **authentication** (identifying the user).
+- the data plane available at `http://127.0.0.1:9080`;
+- the Admin API available at `http://127.0.0.1:9180`;
+- the built-in `authz-casbin` and `mocking` plugins enabled.
+
+You also need `curl` and `jq`. Set `admin_key` to the key configured for your
+Admin API:
+
+```sh
+export admin_key='<your-admin-api-key>'
+```
+
+Replace the placeholder before continuing. Confirm that the Admin API is
+reachable and the plugin is enabled:
+
+```sh
+curl --fail-with-body \
+  "http://127.0.0.1:9180/apisix/admin/plugins/list"; \
+  -H "X-API-KEY: $admin_key" | \
+jq -e '(["authz-casbin", "mocking"] - .) | length == 0'
+```
 
-## Creating a model
+The command should print `true`. Do not publish the Admin API to an untrusted
+network or reuse demonstration credentials in another environment.
 
-The plugin authorizes each request using three parameters: **subject**, 
**object**, and **action**. The subject comes from a header (e.g. `username: 
alice`), the object is the URL path, and the action is the HTTP method.
+## Define the model and policy
 
-Suppose we have three paths: `/`, `/res1`, and `/res2`. We want a model like 
this:
+The plugin evaluates three request values:
 
-![image](/img/blog/model.png)
+- `sub`: the subject from the configured request header;
+- `obj`: the request URI path; and
+- `act`: the HTTP method.
 
-So: any user (e.g. `jack`) can access `/`; users with the `admin` role (e.g. 
`alice`, `bob`) can access everything; and non-admin users are limited to 
`GET`. Here is a model that does that:
+Create `model.conf`:
 
 ```ini
 [request_definition]
@@ -42,141 +101,170 @@ e = some(where (p.eft == allow))
 m = (g(r.sub, p.sub) || keyMatch(r.sub, p.sub)) && keyMatch(r.obj, p.obj) && 
keyMatch(r.act, p.act)
 ```
 
-## Creating a policy
-
-For the scenario above, the policy could be:
+Create `policy.csv`:
 
 ```csv
-p, *, /, GET
+p, *, /anything, GET
 p, admin, *, *
 g, alice, admin
-g, bob, admin
 ```
 
-The matcher means:
+This policy allows any subject to send `GET /anything`. It also assigns
+`alice` the `admin` role, which can access every path with every method.
+
+## Store the shared policy in plugin metadata
+
+Load the files as JSON strings and write them to APISIX plugin metadata:
+
+```sh
+jq -n \
+  --rawfile model model.conf \
+  --rawfile policy policy.csv \
+  '{model: $model, policy: $policy}' | \
+curl --fail-with-body \
+  "http://127.0.0.1:9180/apisix/admin/plugin_metadata/authz-casbin"; \
+  -H "X-API-KEY: $admin_key" \
+  -H "Content-Type: application/json" \
+  -X PUT \
+  --data-binary @-
+```
 
-1. **`(g(r.sub, p.sub) || keyMatch(r.sub, p.sub))`** — The request subject 
either has the policy subject as a role or matches it via `keyMatch`. For 
`keyMatch` and other built-ins, see [Lua Casbin 
BuiltInFunctions](https://github.com/apache/casbin-lua-casbin/blob/master/src/util/BuiltInFunctions.lua).
-2. **`keyMatch(r.obj, p.obj)`** — The request path matches the policy object.
-3. **`keyMatch(r.act, p.act)`** — The request method matches the policy action.
+Plugin metadata provides one model and policy to routes that enable
+`authz-casbin` without defining their own route-local model and policy.
 
-## Enabling the plugin on a route
+## Enable authorization on a route
 
-After creating the model and policy, enable the plugin on a route via the 
APISIX Admin API. Using **file paths**:
+Create a route and tell the plugin to read the subject from the `user` header:
 
 ```sh
-curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: 
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
-{
+curl --fail-with-body \
+  "http://127.0.0.1:9180/apisix/admin/routes/casbin-demo"; \
+  -H "X-API-KEY: $admin_key" \
+  -H "Content-Type: application/json" \
+  -X PUT \
+  -d '{
+    "uri": "/*",
     "plugins": {
-        "authz-casbin": {
-            "model_path": "/path/to/model.conf",
-            "policy_path": "/path/to/policy.csv",
-            "username": "username"
-        }
-    },
-    "upstream": {
-        "nodes": {
-            "127.0.0.1:1980": 1
+      "authz-casbin": {
+        "username": "user"
+      },
+      "mocking": {
+        "_meta": {
+          "priority": 1000
         },
-        "type": "roundrobin"
+        "response_status": 200,
+        "response_example": "{\"message\":\"authorized\"}"
+      }
     },
-    "uri": "/*"
-}'
+    "upstream": {
+      "type": "roundrobin",
+      "nodes": {
+        "127.0.0.1:1": 1
+      }
+    }
+  }'
 ```
 
-The `username` field is the **header name** that carries the subject (e.g. if 
the header is `user: alice`, set `"username": "user"`).
+The route uses the model and policy from plugin metadata because it only
+defines the `username` field locally. A route-local model and policy, when
+configured, take precedence over plugin metadata.
 
-To use **inline** model and policy text instead of files, use the `model` and 
`policy` fields:
+The `mocking` plugin gives allowed requests a deterministic local response, so
+the example does not depend on a public upstream. Its route-local priority is
+lower than `authz-casbin`, which makes the authorization decision run first.
+The unreachable upstream is therefore not contacted. This priority override
+and mock response are test fixtures, not production recommendations.
 
-```sh
-curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: 
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
-{
-    "plugins": {
-        "authz-casbin": {
-            "model": "[request_definition]
-            r = sub, obj, act
+## Verify allow and deny decisions
 
-            [policy_definition]
-            p = sub, obj, act
+An anonymous request to the public path is allowed:
 
-            [role_definition]
-            g = _, _
+```sh
+curl -i http://127.0.0.1:9080/anything
+```
 
-            [policy_effect]
-            e = some(where (p.eft == allow))
+Expected status:
 
-            [matchers]
-            m = (g(r.sub, p.sub) || keyMatch(r.sub, p.sub)) && keyMatch(r.obj, 
p.obj) && keyMatch(r.act, p.act)",
+```text
+HTTP/1.1 200 OK
+{"message":"authorized"}
+```
 
-            "policy": "p, *, /, GET
-            p, admin, *, *
-            g, alice, admin
-            g, bob, admin",
+`bob` does not have the `admin` role, so a different path is denied:
 
-            "username": "username"
-        }
-    },
-    "upstream": {
-        "nodes": {
-            "127.0.0.1:1980": 1
-        },
-        "type": "roundrobin"
-    },
-    "uri": "/*"
-}'
+```sh
+curl -i http://127.0.0.1:9080/anything/res -H 'user: bob'
 ```
 
-## Using a global model and policy
+Expected result:
+
+```text
+HTTP/1.1 403 Forbidden
+{"message":"Access Denied"}
+```
 
-To use one model and policy for **all** routes, store them in the plugin’s 
metadata. Send a `PUT` request:
+`alice` has the `admin` role, so the same request is allowed:
 
 ```sh
-curl http://127.0.0.1:9080/apisix/admin/plugin_metadata/authz-casbin -H 
'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -i -X PUT -d '
-{
-"model": "[request_definition]
-r = sub, obj, act
+curl -i http://127.0.0.1:9080/anything/res -H 'user: alice'
+```
 
-[policy_definition]
-p = sub, obj, act
+Expected status:
 
-[role_definition]
-g = _, _
+```text
+HTTP/1.1 200 OK
+{"message":"authorized"}
+```
 
-[policy_effect]
-e = some(where (p.eft == allow))
+## Update the shared policy
 
-[matchers]
-m = (g(r.sub, p.sub) || keyMatch(r.sub, p.sub)) && keyMatch(r.obj, p.obj) && 
keyMatch(r.act, p.act)",
+Add a direct permission for `bob` to `policy.csv`:
 
-"policy": "p, *, /, GET
+```csv
+p, *, /anything, GET
 p, admin, *, *
+p, bob, /anything/res, GET
 g, alice, admin
-g, bob, admin"
-}'
 ```
 
-Then enable the plugin on a route (it will use the metadata). Example:
+Run the plugin metadata command again. Routes that reference this metadata use
+the updated policy without changing their route configuration. Repeat the
+request:
 
 ```sh
-curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: 
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
-{
-    "plugins": {
-        "authz-casbin": {
-            "username": "username"
-        }
-    },
-    "upstream": {
-        "nodes": {
-            "127.0.0.1:1980": 1
-        },
-        "type": "roundrobin"
-    },
-    "uri": "/route1/*"
-}'
+curl -i http://127.0.0.1:9080/anything/res -H 'user: bob'
+```
+
+The expected result is now:
+
+```text
+HTTP/1.1 200 OK
+{"message":"authorized"}
 ```
 
-The route then uses the shared model and policy from metadata. To change them, 
send another `PUT` to the plugin metadata; all routes using it will pick up the 
update.
+## Choosing the configuration scope
 
-## Use cases
+Use plugin metadata when several routes should share one model and policy. Use
+a route-local model and policy when a route needs an independent authorization
+shape. For the complete configuration schema and file-based alternative, see
+the [APISIX `authz-casbin` plugin 
reference](https://apisix.apache.org/docs/apisix/plugins/authz-casbin/).
 
-- **Per-route authorization** — Attach the plugin to any route with your model 
and policy. Good when different routes need different permissions or when 
policies are large (each route only loads what it needs).
-- **Global model/policy** — Store one model and policy in plugin metadata and 
reference it from many routes. Updating policy in one place (e.g. etcd) updates 
all those routes.
+## Clean up
+
+Remove the test route:
+
+```sh
+curl --fail-with-body \
+  "http://127.0.0.1:9180/apisix/admin/routes/casbin-demo"; \
+  -H "X-API-KEY: $admin_key" \
+  -X DELETE
+```
+
+Remove the shared plugin metadata:
+
+```sh
+curl --fail-with-body \
+  "http://127.0.0.1:9180/apisix/admin/plugin_metadata/authz-casbin"; \
+  -H "X-API-KEY: $admin_key" \
+  -X DELETE
+```
diff --git a/blog/authors.yml b/blog/authors.yml
index 23100d0e1..ba81bca3d 100644
--- a/blog/authors.yml
+++ b/blog/authors.yml
@@ -32,3 +32,9 @@ aravindarc:
   name: Aravinda Kumar
   url: https://github.com/aravindarc
   image_url: /img/authors/aravindarc.png
+
+yilialin:
+  name: Yilia Lin
+  title: Apache APISIX Committer
+  url: https://github.com/Yilialinn
+  image_url: https://avatars.githubusercontent.com/u/114121331?v=4
diff --git a/docs/Tutorial.mdx b/docs/Tutorial.mdx
index eb2d7d12d..4adb28c7d 100644
--- a/docs/Tutorial.mdx
+++ b/docs/Tutorial.mdx
@@ -121,9 +121,9 @@ The paper describes Casbin’s design in depth. To cite it in 
academic work, use
 <TabItem value="Lua" label="Lua">
 ```
 
-### APISIX
+### Apache APISIX
 
-- [Authorization in APISIX using 
Casbin](https://medium.com/@rushitote/authorization-in-apisix-using-casbin-59b693669d6d)
+- [Authorization in APISIX Using 
Casbin](/blog/2021/08/19/apisix-casbin-authorization/)
 
 ```mdx-code-block
 </TabItem>
diff --git a/src/tableData/MiddlewareData/MiddlewareLuaData.js 
b/src/tableData/MiddlewareData/MiddlewareLuaData.js
index d154f2d68..586d8906f 100644
--- a/src/tableData/MiddlewareData/MiddlewareLuaData.js
+++ b/src/tableData/MiddlewareData/MiddlewareLuaData.js
@@ -12,9 +12,9 @@ export const MiddlewareLuaData = [
     image: "/img/ecosystem/openResty.png",
   },
   {
-    title: "[APISIX](https://github.com/apache/apisix)",
+    title: "[Apache APISIX](https://github.com/apache/apisix)",
     description:
-      "A dynamic, real-time, high-performance API gateway, via plugin: 
[authz-casbin](https://github.com/apache/apisix/blob/master/docs/en/latest/plugins/authz-casbin.md)",
+      "An Apache API gateway with a built-in 
[authz-casbin](https://apisix.apache.org/docs/apisix/plugins/authz-casbin/) 
plugin for route-level authorization.",
     image: "/img/ecosystem/apisix.png",
   },
 ].map((item) => {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to