当前位置: 首页 > 工具软件 > kong-oidc > 使用案例 >

在Kong网关中使用JWT认证

盖翰池
2023-12-01

在Kong网关中使用JWT认证

前言

Kong网关快速入门指南 一文中介绍了Key认证和HMAC认证方式,本文介绍在Kong网关中使用JWT认证。

JWT介绍

参见:

简单归纳一下JWT(JSON Web Token)的特点:

  • 是一种JSON格式(未对Token本身Base64编码前)的Token
  • JSON格式,数据更紧凑,网络传输数据量小,编码解码速度快,前后端多种编程语言都会JSON有很好的支持
  • 基于Token的认证已经成为认证的主流方式(较早之前的认证是基于Session的),更安全,在Web和App上都可以使用。
  • Token采用消息发送方和接收方都知道的shared secret来作HmacSHA签名和验签,安全程度高,即使泄漏Token也无法破解secret。
  • Token有有效期,可有效降低被重放攻击(replay attack)的风险
  • Token本身可内省(introspection),Resource Server只要有shared secret无需调用Authorization Server的内省(introspection)接口也可以验签。
  • 相比HMAC认证,更简单。

JWT认证

添加JWT插件

在Service下添加JWT插件,全部保持默认值。

查看插件详情:

uri param names: `jwt`
key claim name: `iss`
secret is base64: NO #NO表示不对secret作Base64编码
headers names: `authorization`

说明:

  • 把后面要生成的JWT credential的key作为JWT payload中的iss 字段(issuer缩写,令牌颁发者)。

生成Consumer的JWT credentials

保持全部默认值来生成Consumer的JWT credentials。

示例:

{
  "tags": null,
  "secret": "pdDcyjZUrOaUY2HDIrrgRnG7ix0hNf7B",
  "consumer": {
    "id": "c3202163-d7b8-4604-8137-466109590fd2"
  },
  "algorithm": "HS256",
  "rsa_public_key": null,
  "id": "e23be144-26b9-4f3a-8938-ceddc34d2e23",
  "created_at": 1626763334,
  "key": "5D4Z0wB7A8RXVsfivcEZ2VlRGGlF09Lf"
}

说明:

  • key 为JWT payload里面的iss (issuer的缩写,表示令牌颁发者)
  • secret 为签名时用的密钥。
  • "algorithm": "HS256" 表示用HmacSHA256算法。

测试

在Postman上新建Environment Variable:

  • jwt_secret:pdDcyjZUrOaUY2HDIrrgRnG7ix0hNf7B - 对应JWT credential的secret
  • jwt_iss:5D4Z0wB7A8RXVsfivcEZ2VlRGGlF09Lf - 对应JWT credential的key

在Request的Authorization中选择Type为“Bearer Token”,Token值为{{jwt_signed}}

Request的Pre-request Script如下:

// JWT generation script adapted from
// https://gist.github.com/corbanb/db03150abbe899285d6a86cc480f674d

var jwtSecret = pm.environment.get('jwt_secret') || ''

// Set headers for JWT
var header = {
	'typ': 'JWT',
	'alg': 'HS256'
};

// Prepare timestamp in seconds
var currentTimestamp = Math.floor(Date.now() / 1000)

var data = {
	'iss': pm.environment.get('jwt_iss') || '',
	'ist': pm.environment.get('jwt_ist') || '',
	'iat': currentTimestamp,
	'exp': currentTimestamp + 30, // expiry time is 30 seconds from time of creation
	'jti': 'jwt_nonce'
}


function base64url(source) {
    // Encode in classical base64
    encodedSource = CryptoJS.enc.Base64.stringify(source)
    
    // Remove padding equal characters
    encodedSource = encodedSource.replace(/=+$/, '')
    
    // Replace characters according to base64url specifications
    encodedSource = encodedSource.replace(/\+/g, '-')
    encodedSource = encodedSource.replace(/\//g, '_')
    
    return encodedSource
}

// encode header
var stringifiedHeader = CryptoJS.enc.Utf8.parse(JSON.stringify(header))
var encodedHeader = base64url(stringifiedHeader)

// encode data
var stringifiedData = CryptoJS.enc.Utf8.parse(JSON.stringify(data))
var encodedData = base64url(stringifiedData)

// build token
var token = `${encodedHeader}.${encodedData}`

// sign token
var signature = CryptoJS.HmacSHA256(token, jwtSecret)
signature = base64url(signature)
var signedToken = `${token}.${signature}`

pm.environment.set('jwt_signed', signedToken)
console.log('Signed and encoded JWT', signedToken)

说明:

  • 'exp': currentTimestamp + 30, - 30s有效时间,可以根据实际情况调整,一般不超过几分钟。

参见:

生成的JWT token示例如下:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiI1RDRaMHdCN0E4UlhWc2ZpdmNFWjJWbFJHR2xGMDlMZiIsImlzdCI6IiIsImlhdCI6MTYyNjc2OTM1MSwiZXhwIjoxNjI2NzY5MzgxLCJqdGkiOiJqd3Rfbm9uY2UifQ.rFisEEzpWfMVhO82G_ktUr_ojcer-MkC30jyBbjBll4

可复制该值到https://jwt.io/ 中查看解码结果。
示例:

Header:

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

Payload:

{
  "iss": "5D4Z0wB7A8RXVsfivcEZ2VlRGGlF09Lf",
  "ist": "",
  "iat": 1626769351,
  "exp": 1626769381,
  "jti": "jwt_nonce"
}

JWT认证的其他用法

上面的过程只适合于一个Service只有有限几个Consumer的情况。

还可以结合OpenID Connect + JWT将每个用户都当作一个Consumer来作更细粒度的JWT认证。

参见:

可惜的是Kong OpenID Connect Plugin只有在Kong收费版上才有,虽然网络上有一个开源的kong-oidc 插件项目,但是已经有几年没有维护了。

参考文档

 类似资料: