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
96 changes: 79 additions & 17 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 lrucache = core.lrucache.new({
Expand All @@ -45,6 +46,8 @@ local consumer_schema = {
properties = {
key = {type = "string"},
secret = {type = "string"},
public_key = {type = "string"},
private_key= {type = "string"},
algorithm = {
type = "string",
enum = {"HS256", "HS512", "RS256"},
Expand Down Expand Up @@ -103,10 +106,19 @@ function _M.check_schema(conf, schema_type)
end

if schema_type == core.schema.TYPE_CONSUMER then
if not conf.secret then
if conf.algorithm ~= "RS256" and not conf.secret then
conf.secret = ngx_encode_base64(resty_random.bytes(32, true))
end

if conf.algorithm == "RS256" then
if not conf.public_key then
return false, "missing valid public key"
end
if not conf.private_key then
return false, "missing valid private key"
end
end

if not conf.exp then
conf.exp = 60 * 60 * 24
end
Expand Down Expand Up @@ -151,6 +163,64 @@ local function get_secret(conf)
end


local function sign_jwt_with_HS(key, auth_conf)
local auth_secret = get_secret(auth_conf)
local ok, jwt_token = pcall(jwt.sign, _M,
auth_secret,
{
header = {
typ = "JWT",
alg = auth_conf.algorithm
},
payload = {
key = key,
exp = ngx_time() + auth_conf.exp
}
}
)
if not ok then
core.log.warn("failed to sign jwt, err: ", jwt_token.reason)
core.response.exit(500, "failed to sign jwt")
end
return jwt_token
end


local function sign_jwt_with_RS256(key, auth_conf)
local ok, jwt_token = pcall(jwt.sign, _M,
auth_conf.private_key,
{
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, err: ", jwt_token.reason)
core.response.exit(500, "failed to sign jwt")
end
return jwt_token
end


local function algorithm_handler(consumer)
if not consumer.auth_conf.algorithm or consumer.auth_conf.algorithm == "HS256"
or consumer.auth_conf.algorithm == "HS512" then
return sign_jwt_with_HS, get_secret(consumer.auth_conf)
elseif consumer.auth_conf.algorithm == "RS256" then
return sign_jwt_with_RS256, consumer.auth_conf.public_key
end
end


function _M.rewrite(conf, ctx)
local jwt_token, err = fetch_jwt_token(ctx)
if not jwt_token then
Expand Down Expand Up @@ -186,9 +256,10 @@ function _M.rewrite(conf, ctx)
end
core.log.info("consumer: ", core.json.delay_encode(consumer))

local auth_secret = get_secret(consumer.auth_conf)
local _, auth_secret = algorithm_handler(consumer)
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}
end
Expand Down Expand Up @@ -224,22 +295,13 @@ local function gen_token()

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

local auth_secret = get_secret(consumer.auth_conf)
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
}
}
)
local sign_handler, _ = algorithm_handler(consumer)
local jwt_token = sign_handler(key, consumer.auth_conf)
if jwt_token then
return core.response.exit(200, jwt_token)
end

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