F5社区-F5技术交流中心

Nginx Plus 设置JWT身份验证

2020-07-04 20:51:01

Kimi Guo

NGINX Plus是在开源NGINX之上构建的负载均衡、web服务和内容缓存等专业级软件产品。NGINX Plus除了开放源代码产品中提供的所有功能外,还具有独有的企业级功能与服务,包括会话持久性、通过API进行配置、主动健康状况检查、安全增强等功能。使用NGINX Plus可以在任何运行环境下获得创新的自由。

 

在安全控制特性部分,NGINX Plus比开源NGINX增加了JWT authenticationOpenID Connect SSONGINX Web Application Firewall (需要额外开销)部分,更好的满足用户需求。

安全控制特性

NGINX OSS

NGINX Plus

HTTP Basic Authentication

‘Yes’

‘Yes’

HTTP authentication subrequests

‘Yes’

‘Yes’

IP addressbased access control lists

‘Yes’

‘Yes’

Rate limiting

‘Yes’

‘Yes’

Dual-stack RSA/ECC SSL/TLS offload

‘Yes’

‘Yes’

TLS 1.3 support

‘Yes’

‘Yes’

JWT authentication

 

‘Yes’

OpenID Connect SSO

 

‘Yes’

NGINX Web Application Firewall (需要额外开销)

 

‘Yes’

 














前提条件

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/rs256RSASSA-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-384HMAC

        HS512 jwk/hs512(使用 SHA-512 HMAC

        RS256 jwk/rs256RSASSA-PKCS1-v1_5使用SHA-256

        RS384 jwk/rs384RSASSA-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-384SHA-384ECDSA

 

注意:原生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

 

headerJWT的头部包含两部分信息:

  • 声明类型,这里是jwt
  • 声明加密的算法,通常使用HMAC SHA‑256

payload:载荷就是存放有效信息的地方,这些有效信息包含三个部分:

  • 标准中注册的声明
  • 公共的声明
  • 私有的声明

signature:签证信息,由三部分组成:

  • headerbase64加密后的)
  • payloadbase64加密后的)
  • secret

完整的JWT示例如下所示(我们添加了换行符以提高可读性和颜色以区分这三个部分):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJsYzEiLCJlbWFpbCI6ImxpYW0uY3JpbGx5QG5naW54LmNvbSIsImV4cCI6IjE0ODMyMjg3OTkifQ.VGYHWPterIaLjRi0LywgN3jnDUQbSsFptUw99g2slfc

       

当我们对示例JWT进行解码时可以看到该算法:

 

Encoded 

Decoded

header

eyJhbGciOiJIUzI1NiIsInR5cCI6Ik
pXVCJ9

{
    "alg": "HS256",
    "typ": "JWT"
}

playload

eyJzdWIiOiJsYzEiLCJlbWFpbCI6ImxpYW0uY3JpbGx5QG5naW54LmNvbSIsImV4cCI6IjE0ODMyMjg3OTkifQ

{

    "sub": "lc1",

    "email": "liam.crilly@nginx.com",

    "exp": "1483228799"

}

 

JWT作为API密钥

认证API客户端(请求API资源的远程软件客户端)的常见方法是通过共享密钥,通常称为API密钥。传统的API密钥本质上是一个长而复杂的密码,客户端将其作为每个请求的附加HTTP标头发送。如果提供的API密钥在有效密钥列表中,则API端点将授予对请求资源的访问权限。通常,API端点不验证API密钥本身。相反,API网关处理身份验证过程,并将每个请求路由到适当的端点。除了计算分流之外,这还提供了反向代理附带的好处,例如高可用性和对许多API端点的负载均衡。

API客户端和具有传统API密钥的JWT身份验证API网关通过在将请求传递到API端点之前咨询密钥注册表来验证API密钥

 

通常将不同的访问控制和策略应用于不同的API客户端。对于传统的API密钥,这需要查找以将API密钥与一组属性匹配。在每个请求上执行此查找对系统的整体延迟具有明显的影响。

 

通过使用JWT作为API密钥,将嵌入这些属性,从而无需单独查找,释放资源,并满足无状态的分布式部署环境。将最佳实践身份验证技术与基于标准的架构结合使用,以交换身份属性,从而提供了传统API密钥的高性能、高扩展替代方案。

使用JWT和NGINX Plus进行API客户端和JWT身份验证NGINX Plus在将请求传递到API端点之前先验证JWT

 

NGINX Plus配置为身份验证API网关

 

用于验证 JWTNGINX 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"
}

 

subsubject)字段是name字段中完整值的唯一标识符。exp字段以Unix Epoch time(自197011日起的秒数)定义到期日期。如果有效负载中存在此字段,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命令将JWTBearer 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。使用简单mapif块,我们可以通过将其JWT标记为已撤消来拒绝对API客户端的访问,直到到达JWTexp声明为止,此时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

Login

手机号
验证码
© 2019 F5 Networks, Inc. 版权所有。京ICP备16013763号-1