好的,我想我明白了:-)。
非常感谢慷慨的灵魂在这里提供了所有必要的信息:
所以下面是一个示例空手道特征文件使用
- 一个 RS256 JWT 令牌(放在
x-jwt 标头中)
- mTLS(即使用客户端证书进行双向 TLS)
要做到这一点,需要利用空手道的 JavaScript 和 Java 互操作功能。
这是我们让它工作的设置:
0 $ tree
.
├── karate-config.js
├── karate.jar
├── secrets
│ ├── client-cert-keystore.p12
│ ├── client-cert.pem
│ ├── client-cert_private-key.pem
│ ├── rsa-4096-cert.pem
│ ├── rsa-4096-private.pem
│ └── rsa-4096-public.pem
└── test.feature
1 directory, 9 files
我们将使用 rsa-4096-* 文件的私钥 rsa-4096-private.pem(保密!)来创建签名令牌。
所以 JWT 部分的基本文件是
-
rsa-4096-private.pem 用于创建 JWT
-
rsa-4096-public.pem 用于验证令牌/签名,这就是 api/service/server 对您的 JWT 令牌所做的事情(即我们的功能文件中不需要/使用此文件)。您可以尝试使用例如验证生成的令牌https://jwt.io/。
旁注:公钥/私钥对可以使用例如生成openssl.
作为奖励,此示例包含使用客户端证书和 mTLS(httpbin 可能会优雅地忽略)。如果您不需要这个,您可以简单地从空手道配置文件和命令行中删除 configure ssl... 行和 client_cert_keystore_pass 内容。
空手道功能文件:
# test.feature
Feature: Simple test
Background:
# Several helper functions for creating a RS256 signed JWT token.
# Graciously adapted from:
# JWT generation in Karate, but with HmacSHA256:
# https://github.com/karatelabs/karate/issues/1138#issuecomment-629453412
# Signing with sha256 RSA signature in Java:
# https://www.quickprogrammingtips.com/java/how-to-create-sha256-rsa-signature-using-java.html
* def b64encode_bytes =
"""
function(bytes) {
// Base64-encode `bytes`.
// Returns bytes.
var encoder = Java.type('java.util.Base64')
.getUrlEncoder()
.withoutPadding()
return new java.lang.String(encoder.encode(bytes))
}
"""
# Base64-encode `str`, encodes str to UTF-8 and base64-encodes it.
# Returns bytes.
* def b64encode_str = function(str) {return b64encode_bytes(str.getBytes("UTF-8"))}
* def strip_key_header_footer_ws =
"""
function(key_text) {
// Strip -----BEGIN ... header + footer and all newline characters.
// Returns UTF-8-encoded bytes.
// Need string object for replaceAll method.
var key_text_str = new java.lang.String(key_text)
var key_str = key_text_str
.replaceAll("-----BEGIN PRIVATE KEY-----", "")
.replaceAll("-----END PRIVATE KEY-----", "")
.replaceAll("\r", "")
.replaceAll("\n", "")
return key_str.getBytes('UTF-8')
}
"""
* def sha256rsa_sign =
"""
function(bytes, privateKey) {
var decoder = Java.type('java.util.Base64')
.getDecoder()
var PKCS8EncodedKeySpec = Java.type(
'java.security.spec.PKCS8EncodedKeySpec')
var spec = new PKCS8EncodedKeySpec(decoder.decode(privateKey))
var kf = Java.type('java.security.KeyFactory').getInstance("RSA")
var signature = Java.type('java.security.Signature')
.getInstance("SHA256withRSA")
signature.initSign(kf.generatePrivate(spec))
signature.update(bytes)
var signed = signature.sign()
return signed
}
"""
* def generate_jwt_sha256rsa =
"""
function(payload) {
// Generate JWT from given `payload` object (dict).
// Returns SHA256withRSA-signed JWT token (bytes).
var header_encoded = b64encode_str(
JSON.stringify({alg: "RS256", typ: "JWT"}))
var payload_encoded = b64encode_str(JSON.stringify(payload))
var data_to_sign = header_encoded + '.' + payload_encoded
var signature = b64encode_bytes(
sha256rsa_sign(data_to_sign.getBytes("UTF-8"), privateKey)
)
var token = data_to_sign + '.' + signature
return token
}
"""
# enable X509 client certificate authentication with PKCS12 file
* configure ssl = { keyStore: 'secrets/client-cert-keystore.p12', keyStoreType: 'pkcs12', keyStorePassword: '#(client_cert_keystore_pass)' }
# get private key for JWT generation and API key
* def privateKeyContent = read('secrets/rsa-4096-private.pem')
* def privateKey = strip_key_header_footer_ws(privateKeyContent)
# generate JWT
* def jwt = generate_jwt_sha256rsa({iss: "ExampleApp", exp: "1924902000"})
# put all needed API access credential in the header
* headers { x-jwt: '#(jwt)'}
* url 'https://httpbin.org'
Scenario Outline: get anything
Given path '/anything/<anything_id>'
When method get
Then status 200
Examples:
| anything_id |
| 1 |
空手道配置文件:
// karate-config.js
function fn() {
//var http_proxy = java.lang.System.getenv('http_proxy');
var client_cert_keystore_pass = java.lang.System.getenv(
'CLIENT_CERT_KEYSTORE_PASS');
// setup connection
karate.configure('connectTimeout', 5000);
karate.configure('readTimeout', 5000);
//karate.configure('proxy', http_proxy);
var config = {
client_cert_keystore_pass: client_cert_keystore_pass
};
return config;
}
如前所述,除非您需要 mTLS,否则您不需要 client_cert_keystore_pass 的东西。此外,您可能不需要超时配置。我已经在代理后面进行了测试,因此它还包含对http_proxy 的一些额外配置支持(已评论,出于教育目的而保留)。适应你的口味。
运行它:
0 $ CLIENT_CERT_KEYSTORE_PASS="$PASSWORD" java -jar karate.jar -o /tmp/karate-out test.feature
17:34:41.614 [main] INFO com.intuit.karate - Karate version: 1.2.1.RC1
17:34:42.076 [main] DEBUG com.intuit.karate.Suite - [config] karate-config.js
17:34:43.942 [main] DEBUG com.intuit.karate - key store key count for secrets/client-cert-keystore.p12: 1
17:34:44.535 [main] DEBUG com.intuit.karate - request:
1 > GET https://httpbin.org/anything/1
1 > x-jwt: eyJhbGciO...
1 > Host: httpbin.org
1 > Connection: Keep-Alive
...
---------------------------------------------------------
feature: test.feature
scenarios: 1 | passed: 1 | failed: 0 | time: 1.7300
---------------------------------------------------------
17:34:46.577 [main] INFO com.intuit.karate.Suite - <<pass>> feature 1 of 1 (0 remaining) test.feature
Karate version: 1.2.1.RC1
======================================================
elapsed: 4.74 | threads: 1 | thread time: 1.73
features: 1 | skipped: 0 | efficiency: 0.36
scenarios: 1 | passed: 1 | failed: 0
======================================================
HTML report: (paste into browser to view) | Karate version: 1.2.1.RC1
file:///tmp/karate-out/karate-reports/karate-summary.html
===================================================================
0 $
请注意,我绝不是空手道专家,也不是 JavaScript 或 Java 程序员。所以这可能不是你惯用的空手道/JS/Java 代码。 ;-)