目录

Apisix Jwt Plugin With Rsa

Apisix Jwt Plugin With Rsa

configration

先按照官方的指引,安装并配置好 apisix 服务端。然后按照本文开始配置 jwt 以及使用 jwt 进行接口授权访问。本文内涉及 key 以及密码、token之类都经过删减,需替换成你实际环境的参数。

https://apisix.apache.org/zh/docs/apisix/plugins/jwt-auth/

一、先增加一个 consumer:

consumer 的jwt-auth插件 有 3 个关键概念:

  • key : 调用 jwt api 接口时用来区分 consumer,每个 consumer的 key 必须唯一。
  • algorithm:用来对 jwt token 进行加密的加密算法。
  • secret: 用来对 jwt 的 token 进行加密的密钥。
    • 如果是对称加密,需要告诉 api 用户该密钥进行验证,否则需要到服务器进行验证。
    • 如果是非对称加密,只需要告诉 api 用户公钥,签发 token 使用私钥,用户公钥验证即可。提高安全性和验证性能。

这里演示两种加密方式:

  • 一个使用HS256, consumer名称APISIX ;对称加密,
  • 一个使用使用rs256 ,consumer名称apisixRS256;非对称加密。

在 consumer 页面中添加 jwt-auth 插件:

Consumer 类型1 : 使用 HS256 加密jwt,consumer名称:APISIX。

Consumer 创建 Consumer 并开启 jwt-auth 插件,才可以使用 jwt-auth 插件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
curl -XPUT 'http://127.0.0.1:9080/apisix/admin/consumers' \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' \
-H 'Content-Type: application/json' \
-d '{
    "username": "APISIX",
    "plugins": {
        "jwt-auth": {
            "key": "180029",
            "algorithm": "HS256"
            "secret": "mysecretkey"
        }
    }
}'

Consumer 类型2 : 使用rs256加密jwt,consumer名称:apisixRS256

生成一个私钥证书(做签发用)

1
openssl genrsa -out private.key 2048

基于私钥生成一个公钥证书(做验证用,可以发给 api 用户)

1
openssl rsa -in private.key -pubout > public.key

创建 Consumer 并开启 jwt-auth 插件 。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
curl -XPUT 'http://127.0.0.1:9080/apisix/admin/consumers' \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' \
-H 'Content-Type: application/json' \
-d '{
    "username": "apisixRS256",
    "plugins": {
        "jwt-auth": {
            "key": "180029RS256",
            "public_key": "-----BEGIN PUBLIC KEY-----\n\n-----END PUBLIC KEY-----\n",
            "private_key": "-----BEGIN RSA PRIVATE KEY-----\n \n-----END RSA PRIVATE KEY-----\n",
            "algorithm": "RS256"
        }
    }
}'

二、创建 /apisix/plugin/jwt/sign 的 Route

该 api 路径主要用来进行 jwt token的签发。/apisix/plugin/jwt/sign 是 jwt-auth 固定的 uri,我们需要把他暴露出来,客户端才能访问到。这样也无需自己再实现一遍 jwt token 的签发功能。

根据前提条件中的 Consumer 创建 Route,设置 uri 为 jwt-auth 插件中签发 JWT 的 API 地址,并在此 Route 中开启 public-api 插件。

1
2
3
4
5
6
7
8
9
curl -XPUT 'http://127.0.0.1:9080/apisix/admin/routes/r1' \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' \
-H 'Content-Type: application/json' \
-d'{
    "uri": "/apisix/plugin/jwt/sign",
    "plugins": {
        "public-api": {}
    }
}'

测试能否正确生成 jwt

使用如下命令进行测试,如果您看到返回结果是一个 JWT 字符串,表示此 public API 已经可以使用。使用不同的key,将会使用不同的consumer, 不同consumer可能使用不同加密方法。

curl -XGET 'http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=user-key'

返回jwt格式: <header>.<payload>.<signature>

经过测试,当请求携带正确的 user-key 时,public API 可以正常响应,而没有携带 user-key 时,将返回 401 未认证的状态码。如果您测试的返回结果和示例状态一致,则证明您刚刚配置的key-auth插件已经生效。

生成jwt时, 如果没有额外的 payload:

使用 hs256 加密 curl http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=180029 -i 返回:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NTA5NTYxODgsImtleSI6IjE4MDAyOSJ9.OXHvG59O_fk_xxxxxxxxlBmKMklXk0

使用rs256 加密。consumer来生成jwt , 注意key是180029RS256 curl http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=180029RS256 -i

eyJ4NWMiOlsiLS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS1NSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXEwSTVVZFI1THpcL09nZU9SMmpTV1xuYjB5Vlh4a3gyUzdXUDFSQllcLzM4THBJV2o1K1diNWFwVTRHTkJTVXZvc3JuWmtJZVlZWFNYajZ5bXoyTXY4eXBcbjU0N1VlUDluSk10bldwdThIVG1ZZCtRNjY4MEV2aWY3UjhWNlFTNmZXZ3ZscDhsaU5PajdkVXB1aFhhYmlLN1FcbnlLclwvRUtcL0FPbUwwUnUyaFwvazM2dnNtSkQwUDloUnQ2bmtGS0haZndOd2MyXC9FQzlkYUsrRHJERUkrOXo1eWtMXG5jVEx1bHlSdzJcL0hEXC9pQlc3N2dmcHJ2YmR2eGhWc1wvaHA3YUJTU0g4R2lscVJkaU80S0JIc0RlY1Jza1J6aWdyXG5TVkdLdDQxTFpBVEY4Z3hGWmRNVFwvZDNQUW1tWlNrM1ZjcjhQenR3ZXVMTlVlTFdJRFdtNHA4enF0bTdXa2t2TlxuNVFJREFRQUJcbi0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLSJdLCJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NTExMTIxOTQsImtleSI6IjE4MDAyOVJTMjU2In0.qj-elpFyzXCIhu6Ikx1jVYFV8tjwrpIgYkUtvytlbty-aHeLn7-K3Amjk7lhGQkzRYMXCsDYMee58xkb34V-4BBlfg61vsDer04zhB2bnxGnIJJWAEA4T8bIq5rJKPou0e1x7BdW266j507EeEcK4gKgUMtcX1izxxxxxxxCYQ5UzM0hQ

生成jwt时,可以添加额外的 payload ,用于传递用户信息。

使用 hs256 加密 curl -G --data-urlencode 'payload={"uid":10000,"uname":"test"}' http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=180029 -i

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.xxDAwLCJrZXkiOiIxODAwMjkiLCJleHAiOjE2NTA5NTYyMjZ9.lwmyZ22pzv6k_DlSX24F4qBcixW7R7-DxgPxKyMaZvk

使用rs256 consumer来生成jwt , 注意key是 180029RS256 curl -G --data-urlencode 'payload={"uid":10000,"uname":"test"}' http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=180029RS256 -i

eyJ4NWMiOlsiLS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS1NSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXEwSTVVZFI1THpcL09nZU9SMmpTV1xuYjB5Vlh4a3gyUzdXUDFSQllcLzM4THBJV2o1K1diNWFwVTRHTkJTVXZvc3JuWmtJZVlZWFNYajZ5bXoyTXY4eXBcbjU0N1VlUDluSk10bldwdThIVG1ZZCtRxxxJ6aWdyXG5TVkdLdDQxTFpBVEY4Z3hGWmRNVFwvZDNQUW1tWlNrM1ZjcjhQenR3ZXVMTlVlTFdJRFdtNHA4enF0bTdXa2t2TlxuNVFJREFRQUJcbi0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLSJdLCJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEwMDAwLCJ1bmFtZSI6InRlc3QiLCJrZXkiOiIxODAwMjlSUzI1NiIsImV4cCI6MTY1MTExMjI1OX0.RMNtfsRWD1FHpaAGieAun3BlblZF6IFtkt6ojk2RYb0Unz2WK3gpSpcQDWp31Mr9DUEPn9hKApK_X5FvUQym1J3dG24UdlUF4qOxxxEzu3LwYdy0n5Q2SdHDIegHQv2habfia3ZrsW7cahcFk8KRgtzB0CEtE3Ag71-e2IrQ0EmpnLZN4EYZ7vDpl3nEjiSYUwEItMqNICJGN-kzUXt_s15Gf3p4r91HK3LC9NzNgUvEcTSsOlfC6OfB1Ngvs7pNRrAMQYBGZ4Q

生成的 jwt token 看起来比较难懂,可以用 https://jwt.io/ 进行解析。上面 180029RS256 的 token 解析后,可以看到 header 为

1
2
3
4
5
6
7
{
  "x5c": [
    "-----BEGIN PUBLIC KEY-----MIIBIjANBxxxxxlp8liNOj7dUpuhXabiK7Q\nyKr/EK/AOmL0Ru2h/k36vsmJD0P9hRt6nkFKHZfwNwc2/EC9daK+DrDEI+9z5ykL\ncTLulyRw2/HD/iBW77gfprvbdvxhVs/hp7aBSSH8GilqRdiOxxxxUeLWIDWm4p8zqtm7WkkvN\n5QIDAQAB\n-----END PUBLIC KEY-----"
  ],
  "alg": "RS256",
  "typ": "JWT"
}

head 带有公钥,验证方可直接使用该 公钥进行token验证,安全性更高。如果使用堆成加密,存在密钥泄露的可能性,被人利用伪造访问接口。

三、创建对外的API服务, 使用 jwt-auth 插件

jwt-token 的作用是用来保护 API 服务,这里我们创建一个受jwt 保护的 Route 或 Service 对象,验证 jwt-token 是否正常使用。这个 route 使用上游127.0.0.1:9900的服务(/index.html),并开启 jwt-auth 插件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
    "methods": ["GET"],
    "uri": "/index.html",
    "plugins": {
        "jwt-auth": {}
    },
    "upstream": {
        "type": "roundrobin",
        "nodes": {
            "127.0.0.1:9900": 1
        }
    }
}'

四、客户端使用 jwt 服务

第一步, 调用 jwt-auth接口获取 token

用户key 180029 对应 apisix consumer。

curl http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=180029 -i

eyJ0eXAiOiJKV1QiLCxxxMDAsImtleSI6IjE4MDAyOSJ9.7BMa3vmjRzs34kbeo-VvoJlH8LRVJJbwRuWbJS1cxMY

用户key 180029RS256 对应 apisixRS256 consumer。

curl http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=180029RS256 -i

eyJ4NWMiOlsiLS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS1cbk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBcTBJNVVkUjVMelwvT2dlT1IyalNXXG5iMHlWWHhreDJTN1dQMVJCWVwvMzhMcElXajUrV2x2NjgwRXZpZjdSOFY2UVM2ZldndmxwOGxpTk9qN2RVcHVoWGFiaUs3UVxueUtyXC9FS1wvQU9tTDBSdTJoXC9rMzZ2c21KRDBQOWhSdDZua0ZLSFpmd053YzJcL0VDOWRhSytEckRFSSs5ejV5xzRGVjUnNrUnppZ3JcblNWR0t0NDFMWkFURjhneEZaZE1UXC9kM1BRbW1aU2szVmNyOFB6dHdldUxOVWVMV0lEV200cDh6cXRtN1dra3ZOXG41UUlEQVFBQlxuLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tXG4iXSwiYWxnIjoiUlMyNTYiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE2NTExMTc4NzgsIxmtleSI6IjE4MDAyOVJTMjU2In0.UdG9rodX4edkHrjN1rMnbteMsbr8im9Vzlol6l97T7Qs-YZAThFoVqxCwJdKIHuGQjDd-XuVvW6zc9kQAR5NH7YRNa1iq5MGnF84WxjxdcsUN24glqhWRGAF1JreG9UybVIign_tNnnqovVphFEAgV09SA-AD1f2tl0XXRTk8up-wgTCWmm08VubH2-jdQ

第二步、使用获取到的 token 进行请求尝试#

先试试不传 token ,返回未授权

curl http://127.0.0.1:9080/index.html HTTP/1.1 401 Unauthorized

{"message":"Missing JWT token in request"}

再试试把 token 放到请求头中:
情景一、使用 hs256 加密算法的 180029 key 用户
1
curl http://127.0.0.1:9080/index.html -H 'Authorization: eyJ0eXAiOiJKV1QiLxciOiJIUzI1NiJ9.eyJleHAiOjE2NTExMTxxtleSI6IjE4MDAyOSJ9.7BMa3vmjRzs34kxx8LRVJJbwRuWbJS1cxMY' -i

返回如下,说明正常

1
2
3
4
5
6
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 10
Connection: keep-alive
Date: Wed, 27 Apr 2022 03:52:03 GMT
Server: APISIX/2.13.1
情景二、使用 rs256 加密算法的 180029rs256 key 用户
1
curl http://127.0.0.1:9080/index.html -H 'Authorization: eyJ4NWMiOlsiLS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS1cbk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBcTBJNVVkUjVMelwvT2dlT1IyalNXXG5iMHlWWHhreDJTN1dQMVJCWVwvMzxxeSI6IjE4MDAyOVJTMjU2In0.UdG9rodX4edkHrjN1rMnbteMsbr8im9Vzlol6l97T7Qs-YZAThFoVqxCwJdKIHuGQjDd-XuVvW6zc9kQAR5NH7YRNa1iq5Mxx1mPydcsUN24glqhWRGAF1JreG9UybVIign_tNnnqovVphFEAgV09SA-AD1f2tl0XXRTk8up-wgTCWmm08VubH2-jdQ' -i

返回如下,说明正常

1
2
3
4
5
6
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 10
Connection: keep-alive
Date: Wed, 27 Apr 2022 03:52:03 GMT
Server: APISIX/2.13.1

结束语

整个配置方法到此结束,涉及 consumer、jwt api route、被保护 api 的创建。在大型场景里面,建议使用非对称加密的算法进行签名校验。会更加安全。