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:
-
+- `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]