Nginx Plus 设置JWT身份验证
2020-07-04 20:51:01
Kimi Guo
NGINX
Plus是在开源NGINX之上构建的负载均衡、web服务和内容缓存等专业级软件产品。NGINX Plus除了开放源代码产品中提供的所有功能外,还具有独有的企业级功能与服务,包括会话持久性、通过API进行配置、主动健康状况检查、安全增强等功能。使用NGINX Plus可以在任何运行环境下获得创新的自由。
在安全控制特性部分,NGINX Plus比开源NGINX增加了JWT authentication、OpenID Connect SSO与NGINX Web Application Firewall (需要额外开销)部分,更好的满足用户需求。
安全控制特性 |
NGINX
OSS |
NGINX
Plus |
HTTP Basic Authentication |
|
|
HTTP authentication subrequests |
|
|
IP address‑based
access control lists |
|
|
Rate limiting |
|
|
Dual-stack RSA/ECC SSL/TLS offload |
|
|
TLS 1.3 support |
|
|
JWT authentication |
|
|
OpenID Connect SSO |
|
|
NGINX Web Application Firewall (需要额外开销) |
|
|
前提条件
NGINX Plus R10及更高版本可以直接使用JWT。在本章中,我们描述了如何使用 NGINX Plus 作为API 网关,为API终结点提供前端服务,并使用 JWT 对客户端应用程序进行身份验证。
最新NGINX Plus R18支持以下加密算法,可以在以下目录中找到JSON Web令牌(JWT)文件
HS256 jwt/hs256(使用 SHA-256 的 HMAC)
HS384 jwt/hs384(使用 SHA-384 的 HMAC)
HS512 jwt/hs512(使用 SHA-512 的 HMAC)
RS256 jwt/rs256(RSASSA-PKCS1-v1_5使用SHA-256)
RS384 jwt/rs384 (RSASSA-PKCS1-v1_5 使用 SHA-384)
RS512 jwt/rs512 (RSASSA-PKCS1-v1_5使用 SHA-512)
ES256 jwt/es256(使用 P-256 和 SHA-256 的 ECDSA)
ES384 jwt/es384(使用 P-384 和 SHA-384 的 ECDSA)
相应的 JSON Web 密钥 (JWK) 位于:
HS256 jwk/hs256(使用 SHA-256 的 HMAC)
HS384 jwk/hs384(使用SHA-384的HMAC)
HS512 jwk/hs512(使用 SHA-512 的 HMAC)
RS256 jwk/rs256(RSASSA-PKCS1-v1_5使用SHA-256)
RS384 jwk/rs384(RSASSA-PKCS1-v1_5使用SHA-384)
RS512 jwk/rs512 (RSASSA-PKCS1-v1_5使用 SHA-512)
ES256 jwk/es256(使用 P-256 和 SHA-256 的 ECDSA)
ES384 jwk/es384(使用P-384和SHA-384的ECDSA)
注意:原生JWT身份验证仅在NGINX Plus中可用。
JWT介绍
JSON Web Token
(JWT) 是一种紧凑且高度可移植的基于JSON的开放标准((RFC 7519)身份信息交换方式。JWT规范是OpenID Connect的重要基础,它为OAuth 2.0生态系统提供了单一登录令牌。JWTs本身也可以用作身份验证凭据,并且能比传统API密钥更好的控制对Web API的访问。
JWT的数据结构
JWT由三部分组成:头部header、载荷payload和签证signature。
header:JWT的头部包含两部分信息:
- 声明类型,这里是jwt
- 声明加密的算法,通常使用HMAC SHA‑256
payload:载荷就是存放有效信息的地方,这些有效信息包含三个部分:
- 标准中注册的声明
- 公共的声明
- 私有的声明
signature:签证信息,由三部分组成:
- header(base64加密后的)
- payload(base64加密后的)
- secret
完整的JWT示例如下所示(我们添加了换行符以提高可读性和颜色以区分这三个部分):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiJsYzEiLCJlbWFpbCI6ImxpYW0uY3JpbGx5QG5naW54LmNvbSIsImV4cCI6IjE0ODMyMjg3OTkifQ
.
VGYHWPterIaLjRi0LywgN3jnDUQbSsFptUw99g2slfc
当我们对示例JWT进行解码时可以看到该算法:
|
Encoded |
Decoded |
header |
|
{ |
playload |
|
{ "sub": "lc1", "email":
"liam.crilly@nginx.com", "exp": "1483228799" } |
JWT作为API密钥
认证API客户端(请求API资源的远程软件客户端)的常见方法是通过共享密钥,通常称为API密钥。传统的API密钥本质上是一个长而复杂的密码,客户端将其作为每个请求的附加HTTP标头发送。如果提供的API密钥在有效密钥列表中,则API端点将授予对请求资源的访问权限。通常,API端点不验证API密钥本身。相反,API网关处理身份验证过程,并将每个请求路由到适当的端点。除了计算分流之外,这还提供了反向代理附带的好处,例如高可用性和对许多API端点的负载均衡。
API网关通过在将请求传递到API端点之前咨询密钥注册表来验证API密钥
通常将不同的访问控制和策略应用于不同的API客户端。对于传统的API密钥,这需要查找以将API密钥与一组属性匹配。在每个请求上执行此查找对系统的整体延迟具有明显的影响。
通过使用JWT作为API密钥,将嵌入这些属性,从而无需单独查找,释放资源,并满足无状态的分布式部署环境。将最佳实践身份验证技术与基于标准的架构结合使用,以交换身份属性,从而提供了传统API密钥的高性能、高扩展替代方案。
NGINX Plus在将请求传递到API端点之前先验证JWT
将NGINX Plus配置为身份验证API网关
用于验证 JWT的NGINX
Plus配置非常简单:
upstream api_server {
server 10.0.0.1;
server 10.0.0.2;
}
server {
listen 80;
location /products/ {
auth_jwt "Products API";
auth_jwt_key_file conf/api_secret.jwk;
proxy_pass http://api_server;
}
}
我们要做的第一件事是在upstream
块中指定托管API端点的服务器的地址。该location
块指定必须对以/ products /开头的URL的任何请求进行身份验证。该auth_jwt
指令定义了认证401
失败时将返回的认证领域(以及状态码)。
该auth_jwt_key_file
指令告诉NGINX Plus如何验证JWT的签名元素。在此示例中,我们使用HMAC SHA‑256算法对JWT进行签名,因此我们需要在conf
/ api_secret.jwk中创建一个JSON Web密钥以包含用于签名的对称密钥。该文件必须遵循JSON Web密钥规范描述的格式;我们的示例如下所示:
{"keys":
[{
"k":"ZmFudGFzdGljand0",
"kty":"oct",
"kid":"0001"
}]
}
对称密钥在该k
字段中定义,这是纯文本字符串的Base64URL编码值fantasticjwt
。我们通过运行以下命令获得编码值:
$
echo -n fantasticjwt | base64 | tr '+/' '-_' |
tr -d '='
该kty
字段将密钥类型定义为对称密钥(八位位组序列)。最后,kid
[Key ID]字段为此JSON Web Key定义了一个序列号,该序列号0001
使我们能够在同一文件中支持多个密钥(由auth_jwt_key_file
指令命名),并管理这些密钥的生命周期以及用它们签名的JWT
。
向API客户端发布JWT
作为示例API客户端,我们将使用“quotation system”应用程序,并为API客户端创建JWT。首先,我们定义JWT标头:
{
"typ":"JWT",
"alg":"HS256",
"kid":"0001"
}
该typ
字段将类型定义为JSON Web令牌,该alg
字段指定JWT使用HMAC SHA256算法签名,并且该kid
字段指定JWT使用带有该序列号的JSON Web密钥签名。
接下来,我们定义JWT有效负载:
{
"name":"Quotation System",
"sub":"quotes",
"exp":"1577836800",
"iss":"My API Gateway"
}
sub
(
subject
)字段是
name
字段中完整值的唯一标识符。
exp
字段以
Unix Epoch time
(自
1970
年
1
月
1
日起的秒数)定义到期日期。如果有效负载中存在此字段,
NGINX Plus
将作为
JWT
验证过程的一部分检查该值,并拒绝过期的
JWT
,即使它们是正确的。
iss
字段描述
JWT
的颁发者,如果
API
网关还接受来自第三方颁发者或集中身份管理系统的
JWT
,则该字段非常有用。
现在我们已经拥有创建JWT所需的一切,我们按照以下步骤正确地对其进行编码和签名。
1.Separately flatten and
Base64URL‑encode the header and payload.
$
echo -n '{"typ":"JWT","alg":"HS256","kid":"0001"}' | base64 | tr '+/' '-_' | tr -d '='
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEifQ
$
echo -n '{"name":"Quotation System","sub":"quotes","exp":"1577836800","iss":"My API Gateway"}' | base64 | tr '+/' '-_' | tr -d '='
eyJuYW1lIjoiUXVvdGF0aW9uIFN5c3RlbSIsInN1YiI6InF1b3RlcyIsImV4cCI6IjE1Nzc4MzY4MDAiLCJpc3MiOiJNeSBBUEkgR2F0ZXdheSJ9
2.Concatenate
the encoded header and payload with a period (.) and assign the result to
the HEADER_PAYLOAD
variable.
$
>HEADER_PAYLOAD=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEifQ.eyJuYW1lIjoiUXVvdGF0aW9uIFN5c3RlbSIsInN1YiI6InF1b3RlcyIsImV4cCI6IjE1Nzc4MzY4MDAiLCJpc3MiOiJNeSBBUEkgR2F0ZXdheSJ9
3. Sign the
header and payload with our symmetric key and Base64URL‑encode the signature.
$
echo -n $HEADER_PAYLOAD | openssl dgst -binary -sha256 -hmac fantasticjwt | base64 | tr '+/' '-_' | tr -d '='
McT4bZb8d8WlDgUQUl7rIEvhr3mQL8Faw_Qy1qfugrQ
4. Append the
encoded signature to the header and payload.
$
echo $HEADER_PAYLOAD.McT4bZb8d8WlDgUQUl7rIEvhr3mQL8Faw_Qy1qfugrQ > quotes.jwt
5. Test
by making an authenticated request to the API gateway (in this example, the
gateway is running on localhost).
$
curl -H "Authorization: Bearer `cat quotes.jwt`" http://localhost/products/widget1
第5步中的curl
命令将JWT以Bearer Token的形式发送到NGINX Plus ,这是NGINX Plus期望的默认值。NGINX Plus还可以从cookie或查询字符串参数获取JWT;要进行配置,请将token=
参数包含在auth_jwt
指令中。例如,使用以下配置,NGINX Plus可以验证通过此curl
命令发送的JWT :
$
curl http://localhost/products/widget1?apijwt=`cat quotes.jwt`
server {
listen 80;
location /products/ {
auth_jwt "Products API"
token=$arg_apijwt;
auth_jwt_key_file conf/api_secret.jwk;
proxy_pass http://api_server;
}
}
一旦配置了NGINX Plus,并如上所述生成并验证了JWT,就可以将JWT发送给API客户端开发人员,并就将用于随每个API请求提交JWT的机制达成一致。
撤销JWT
有时可能需要撤销或重新发布API客户端的JWT。使用简单map
和if
块,我们可以通过将其JWT标记为已撤消来拒绝对API客户端的访问,直到到达JWT的exp
声明为止,此时map
可以安全地删除该JWT 的条目。
map $jwt_claim_sub $jwt_status {
"quotes" "revoked";
"test" "revoked";
default "";
}
server {
listen 80;
location /products/ {
auth_jwt "Products API";
auth_jwt_key_file conf/api_secret.jwk;
if ( $jwt_status = "revoked" ) {
return 403;
}
proxy_pass http://api_server;
}
}
总结
JWT非常适合提供对API的身份验证访问。对于API客户端开发人员而言,它就像传统的API密钥一样易于处理,并且为API网关提供身份信息,否则将需要进行数据库查找。 NGINX Plus根据JWT本身包含的信息为JWT身份验证和复杂的配置解决方案提供支持。与其他API网关功能结合使用,NGINX Plus使您能够提供具有速度,可靠性,可伸缩性和安全性的基于API的服务。
发布评论 加入社群
相关文章

k8s-bigip-ctlr模块协同工作机制解析
宗兆伟
2022-02-08 15:56:02 635

NGINX 代码解读之模块架构及配置解析
宗兆伟
2020-04-06 15:27:05 2287

回复评论
发布评论