Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(jwt-auth): support to use of the RS256 algorithm #2594

Merged
merged 10 commits into from
Nov 6, 2020
86 changes: 65 additions & 21 deletions apisix/plugins/jwt-auth.lua
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ local ngx = ngx
local ngx_time = ngx.time
local sub_str = string.sub
local plugin_name = "jwt-auth"
local pcall = pcall


local schema = {
Expand Down Expand Up @@ -105,6 +106,12 @@ function _M.check_schema(conf, schema_type)
conf.secret = ngx_encode_base64(resty_random.bytes(32, true))
end

if conf.algorithm == "RS256" then
if not conf.public_key or not conf.private_key then
return false, "when using RS256 algorithm, the parameter 'public_key' and 'private_key' is both required."
tzssangglass marked this conversation as resolved.
Show resolved Hide resolved
end
end

if not conf.exp then
conf.exp = 60 * 60 * 24
end
Expand Down Expand Up @@ -185,11 +192,14 @@ function _M.rewrite(conf, ctx)
end
core.log.info("consumer: ", core.json.delay_encode(consumer))

local auth_secret = get_secret(consumer.auth_conf)
if not consumer.auth_conf.algorithm or consumer.auth_conf.algorithm == "HS256"
or consumer.auth_conf.algorithm == "HS512" then
local auth_secret = get_secret(consumer.auth_conf)
jwt_obj = jwt:verify_jwt_obj(auth_secret, jwt_obj)
end
if consumer.auth_conf.algorithm and consumer.auth_conf.algorithm == "RS256" then
tzssangglass marked this conversation as resolved.
Show resolved Hide resolved
auth_secret = consumer.auth_conf.public_key
jwt_obj = jwt:verify_jwt_obj(consumer.auth_conf.public_key, jwt_obj)
end
jwt_obj = jwt:verify_jwt_obj(auth_secret, jwt_obj)
core.log.info("jwt object: ", core.json.delay_encode(jwt_obj))
if not jwt_obj.verified then
return 401, {message = jwt_obj.reason}
Expand All @@ -202,6 +212,48 @@ function _M.rewrite(conf, ctx)
end


local function sign_jwt_with_HS(key, auth_conf)
local auth_secret = get_secret(auth_conf)
local jwt_token = jwt:sign(
tzssangglass marked this conversation as resolved.
Show resolved Hide resolved
auth_secret,
{
header = {
typ = "JWT",
alg = auth_conf.algorithm
},
payload = {
key = key,
exp = ngx_time() + auth_conf.exp
}
}
)
return jwt_token
end


local function sign_jwt_with_RS256(key, auth_conf)
local ok, jwt_token = pcall(jwt.sign, self, auth_conf.private_key,
{
tzssangglass marked this conversation as resolved.
Show resolved Hide resolved
header = {
typ = "JWT",
alg = auth_conf.algorithm,
x5c={
auth_conf.public_key,
}
},
payload = {
key = key,
exp = ngx_time() + auth_conf.exp
}
})
if not ok then
core.log.warn("failed to sign jwt, check the private key and public key of the consumer to whom the key belongs.")
core.response.exit(500, "failed to sign jwt")
end
return jwt_token
end


local function gen_token()
local args = ngx.req.get_uri_args()
if not args or not args.key then
Expand All @@ -218,7 +270,6 @@ local function gen_token()
local consumers = core.lrucache.plugin(plugin_name, "consumers_key",
consumer_conf.conf_version,
create_consume_cache, consumer_conf)

core.log.info("consumers: ", core.json.delay_encode(consumers))
local consumer = consumers[key]
if not consumer then
Expand All @@ -227,25 +278,18 @@ local function gen_token()

core.log.info("consumer: ", core.json.delay_encode(consumer))

local auth_secret = get_secret(consumer.auth_conf)
if consumer.auth_conf.algorithm and consumer.auth_conf.algorithm == "RS256" then
auth_secret = consumer.auth_conf.private_key
if not consumer.auth_conf.algorithm or consumer.auth_conf.algorithm == "HS256"
or consumer.auth_conf.algorithm == "HS512" then
local jwt_token = sign_jwt_with_HS(key,consumer.auth_conf)
tzssangglass marked this conversation as resolved.
Show resolved Hide resolved
core.response.exit(200, jwt_token)
end

if consumer.auth_conf.algorithm == "RS256" then
tzssangglass marked this conversation as resolved.
Show resolved Hide resolved
local jwt_token = sign_jwt_with_RS256(key,consumer.auth_conf)
core.response.exit(200, jwt_token)
tzssangglass marked this conversation as resolved.
Show resolved Hide resolved
end
local jwt_token = jwt:sign(
auth_secret,
{
header = {
typ = "JWT",
alg = consumer.auth_conf.algorithm
},
payload = {
key = key,
exp = ngx_time() + consumer.auth_conf.exp
}
}
)

core.response.exit(200, jwt_token)
return core.response.exit(404)
end


Expand Down
37 changes: 29 additions & 8 deletions doc/plugins/jwt-auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
- [中文](../zh-cn/plugins/jwt-auth.md)

# Summary

- [**Name**](#name)
- [**Attributes**](#attributes)
- [**How To Enable**](#how-to-enable)
Expand All @@ -37,13 +38,15 @@ For more information on JWT, refer to [JWT](https://jwt.io/) for more informatio

## Attributes

| Name | Type | Requirement | Default | Valid | Description |
| ------------- | ------- | ----------- | ------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| key | string | required | | | different `consumer` have different value, it's unique. different `consumer` use the same `key`, and there will be a request matching exception. |
| secret | string | optional | | | encryption key. if you do not specify, the value is auto-generated in the background. |
| Name | Type | Requirement | Default | Valid | Description |
|:--------------|:--------|:------------|:--------|:----------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------|
| key | string | required | | | different `consumer` have different value, it's unique. different `consumer` use the same `key`, and there will be a request matching exception. |
| secret | string | optional | | | encryption key. if you do not specify, the value is auto-generated in the background. |
| public_key | string | optional | | | RSA public key, required when `algorithm` attribute selects `RS256` algorithm. |
| private_key | string | optional | | | RSA private key, required when `algorithm` attribute selects `RS256` algorithm. |
| algorithm | string | optional | "HS256" | ["HS256", "HS512", "RS256"] | encryption algorithm. |
| exp | integer | optional | 86400 | [1,...] | token's expire time, in seconds |
| base64_secret | boolean | optional | false | | whether secret is base64 encoded |
| exp | integer | optional | 86400 | [1,...] | token's expire time, in seconds |
| base64_secret | boolean | optional | false | | whether secret is base64 encoded |

## API

Expand All @@ -67,6 +70,23 @@ curl http://127.0.0.1:9080/apisix/admin/consumers -H 'X-API-KEY: edd1c9f034335f1
}'
```

`jwt-auth` uses the `HS256` algorithm by default, and if you use the `RS256` algorithm, you need to specify the algorithm and configure the public key and private key, as follows:

```shell
curl http://127.0.0.1:9080/apisix/admin/consumers -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"username": "kerouac",
"plugins": {
"jwt-auth": {
"key": "user-key",
"public_key": "-----BEGIN PUBLIC KEY-----\n……\n-----END PUBLIC KEY-----",
"private_key": "-----BEGIN RSA PRIVATE KEY-----\n……\n-----END RSA PRIVATE KEY-----",
"algorithm": "RS256"
}
}
}'
```

you can visit Dashboard `http://127.0.0.1:9080/apisix/dashboard/` and add a Consumer through the web console:

![](../images/plugin/jwt-auth-1.png)
Expand Down Expand Up @@ -168,8 +188,8 @@ Accept-Ranges: bytes
## Disable Plugin

When you want to disable the `jwt-auth` plugin, it is very simple,
you can delete the corresponding json configuration in the plugin configuration,
no need to restart the service, it will take effect immediately:
you can delete the corresponding json configuration in the plugin configuration,
no need to restart the service, it will take effect immediately:

```shell
$ curl http://127.0.0.1:2379/v2/keys/apisix/routes/1 -X PUT -d value='
Expand All @@ -186,3 +206,4 @@ $ curl http://127.0.0.1:2379/v2/keys/apisix/routes/1 -X PUT -d value='
}
}'
```

36 changes: 29 additions & 7 deletions doc/zh-cn/plugins/jwt-auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
- [English](../../plugins/jwt-auth.md)

# 目录

- [**名字**](#名字)
- [**属性**](#属性)
- [**如何启用**](#如何启用)
Expand All @@ -38,13 +39,15 @@
## 属性


| 名称 | 类型 | 必选项 | 默认值 | 有效值 | 描述 |
| ------------- | ------- | ------ | ------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| key | string | 必须 | | | 不同的 `consumer` 对象应有不同的值,它应当是唯一的。不同 consumer 使用了相同的 `key` ,将会出现请求匹配异常。 |
| secret | string | 可选 | | | 加密秘钥。如果您未指定,后台将会自动帮您生成。 |
| algorithm | string | 可选 | "HS256" | ["HS256", "HS512", "RS256"] | 加密算法 |
| exp | integer | 可选 | 86400 | [1,...] | token 的超时时间 |
| base64_secret | boolean | 可选 | false | | 密钥是否为 base64 编码 |
| 名称 | 类型 | 必选项 | 默认值 | 有效值 | 描述 |
|:--------------|:--------|:------|:--------|:----------------------------|:-----------------------------------------------------------------------------------------------|
| key | string | 必须 | | | 不同的 `consumer` 对象应有不同的值,它应当是唯一的。不同 consumer 使用了相同的 `key` ,将会出现请求匹配异常。 |
| secret | string | 可选 | | | 加密秘钥。如果您未指定,后台将会自动帮您生成。 |
| public_key | string | 可选 | | | RSA公钥, `algorithm` 属性选择 `RS256` 算法时必填 |
tzssangglass marked this conversation as resolved.
Show resolved Hide resolved
| private_key | string | 可选 | | | RSA私钥, `algorithm` 属性选择 `RS256` 算法时必填 |
| algorithm | string | 可选 | "HS256" | ["HS256", "HS512", "RS256"] | 加密算法 |
| exp | integer | 可选 | 86400 | [1,...] | token 的超时时间 |
| base64_secret | boolean | 可选 | false | | 密钥是否为 base64 编码 |

## 接口

Expand All @@ -67,6 +70,24 @@ curl http://127.0.0.1:9080/apisix/admin/consumers -H 'X-API-KEY: edd1c9f034335f1
}
}'
```

`jwt-auth` 默认使用 `HS256` 算法,如果使用 `RS256` 算法,需要指定算法,并配置公钥与私钥,示例如下:

```shell
curl http://127.0.0.1:9080/apisix/admin/consumers -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"username": "kerouac",
"plugins": {
"jwt-auth": {
"key": "user-key",
"public_key": "-----BEGIN PUBLIC KEY-----\n……\n-----END PUBLIC KEY-----",
"private_key": "-----BEGIN RSA PRIVATE KEY-----\n……\n-----END RSA PRIVATE KEY-----",
"algorithm": "RS256"
}
}
}'
```

你可以使用浏览器打开 dashboard:`http://127.0.0.1:9080/apisix/dashboard/`,通过 web 界面来完成上面的操作,先增加一个 consumer:
![](../../images/plugin/jwt-auth-1.png)

Expand Down Expand Up @@ -183,3 +204,4 @@ $ curl http://127.0.0.1:2379/v2/keys/apisix/routes/1 -X PUT -d value='
}
}'
```

Loading