独立验证Keycloak访问令牌

Wil*_*iam 3 python jwt keycloak

我正在使用Keycloak处理登录并生成JWT令牌。我需要能够验证要发送给我的REST API服务的访问令牌。最佳实践是使用JWT机密直接验证令牌,而不是将其发送到Keycloak服务器进行验证。有很多Java例子可以做到这一点,但是我需要能够使用python或ruby进行验证。

我尝试了以下python签名验证,但遇到错误,ValueError: Could not unserialize key data. 也尝试在https://jwt.io调试器中输入公钥,但也得到了无效的签名。

#!/usr/bin/env python3

import jwt

# Public key from Keycloak realm -> Keys -> Public Key -> (view)
public_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu77nUtVw7SIIcUTSiStzMPB7BGB/9eS+CpppsUaiyZyWCXlrALT3YdqneSlpX4Ta+0wvhOkKQtoSS8dCH8GIi7esAmfdHetHfRgeDXHAlXo8HIzshUzODg3ysT7j+Ha3eJsO+LNS/omHDhsarP8Z2eThW876iKJCCc/mB76a6u1e4Id+52K5lG++m8Pn4Gs+cqd2sKUKcMJ9CkJ6dBIdGlXHMoOHj4C33SPrEG/vEBv5cu0l5PP3RiBAuaZHpLKzfIiaLOpj/k4dD/weVt5gwTIJn16AEgPD7173Xef0HgoPlQInDFrJwsGpYCnIPZWSxRbvjKkya2Auj0QZyMCrXwIDAQAB"

# Keycloak JWT RS256 access-token
access_token = "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI0LVlJOUlVc2R6NGM0SHoycXczT0xXZ0I0eHc2eFd4T29XdktVT2FvV3FzIn0.eyJqdGkiOiJjYzZkMjM5OS04ZmU5LTQzMWItYjZjMS05NWQxMWUxN2FiZGQiLCJleHAiOjE0ODE1NTQ2NTksIm5iZiI6MCwiaWF0IjoxNDgxNTU0MzU5LCJpc3MiOiJodHRwczovL3BtaS1rZXljbG9hay5zYnZpbXByb3Zlci5jb20vYXV0aC9yZWFsbXMvT3BlbkJFTCIsImF1ZCI6ImJlbG1nciIsInN1YiI6ImU1ODc2OGQxLWU3ODktNGU3Yi04ZGVlLWJjMzYxNzFkZDNhZCIsInR5cCI6IkJlYXJlciIsImF6cCI6ImJlbG1nciIsIm5vbmNlIjoiMzc2ZDdjZmUtZGZkNC00Yzg5LWJlZGEtZTlmOWNmYWNlMTNkIiwiYXV0aF90aW1lIjoxNDgxNTUzMTY0LCJzZXNzaW9uX3N0YXRlIjoiN2E2OTEzYzItZWJkZC00MTc2LWI4YTAtZDc2NzVhNDZkNmJjIiwiYWNyIjoiMSIsImNsaWVudF9zZXNzaW9uIjoiOTMyNGMzMjAtMmE4Zi00ODBlLTg5MjItZGQxNmFmMDQxZDdmIiwiYWxsb3dlZC1vcmlnaW5zIjpbIioiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsInZpZXctcHJvZmlsZSJdfX0sIm5hbWUiOiJXaWxsaWFtIEhheWVzIiwicHJlZmVycmVkX3VzZXJuYW1lIjoid2lsbGlhbS5zLmhheWVzQGdtYWlsLmNvbSIsImdpdmVuX25hbWUiOiJXaWxsaWFtIiwiZmFtaWx5X25hbWUiOiJIYXllcyIsImVtYWlsIjoid2lsbGlhbS5zLmhheWVzQGdtYWlsLmNvbSJ9.Q7s-qTcJyH69Ebof8pQI1kZzeT8olwQnRJ06uas5TP2isacxOheHnJ9ixEvqTrr-iefmYMwx41jM68NCs6l8IBNHqv7t5-ediizx4ianMiXr7oZ_1oAT9hkLyrpv9iF2IZBtzNJz0GQAnDYe1moLOLuzqwvcUaWgmzRY95xvzo4kbE8OkeZiMpD_cDmp3_vKOsdn3B6ybJ9TXtea55A29pQzsvAM_6lHeyxTCisipOtu_ubnUOamkYSpxLwWZXgI1w7iz-igt-n7xtlFhUpra239yn9uly9iuBtlgnc3TFDmZn-XRq_PODDJNJeaQXDRaDqnRQhXsoObxCaPqXDQ3A"

access_token_json = jwt.decode(access_token, public_key)
print(access_token_json)
Run Code Online (Sandbox Code Playgroud)

Wil*_*iam 5

为了验证访问令牌,我做了以下事情:

  • 我重置了到期时间范围,因此我不必担心超时是一个复杂的因素。

  • 我必须将BEGIN / END标头/页脚添加到pubkey(基于pubkey的编码,有两种不同的版本-例如'BEGIN RSA PUBLIC KEY'不是Keycloak pubkey编码的正确版本):

-----开始公钥-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu77nUtVw7SIIcUTSiStzMPB7BGB / 9ES + CpppsUaiyZyWCXlrALT3YdqneSlpX4Ta + 0wvhOkKQtoSS8dCH8GIi7esAmfdHetHfRgeDXHAlXo8HIzshUzODg3ysT7j + Ha3eJsO + LNS / omHDhsarP8Z2eThW876iKJCCc / mB76a6u1e4Id + 52K5lG ++ m8Pn4Gs + cqd2sKUKcMJ9CkJ6dBIdGlXHMoOHj4C33SPrEG / vEBv5cu0l5PP3RiBAuaZHpLKzfIiaLOpj / k4dD / weVt5gwTIJn16AEgPD7173Xef0HgoPlQInDFrJwsGpYCnIPZWSxRbvjKkya2Auj0QZyMCrXwIDAQAB
-----结束公钥-----
  • 最后-我必须在Python脚本的jwt.decode中添加Audience ='belmgr'
    ## Python脚本 
    #!/ usr / bin / env python3

    进口jwt

    public_key =“”“ ---开始公共密钥-----
    MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu77nUtVw7SIIcUTSiStzMPB7BGB / 9ES + CpppsUaiyZyWCXlrALT3YdqneSlpX4Ta + 0wvhOkKQtoSS8dCH8GIi7esAmfdHetHfRgeDXHAlXo8HIzshUzODg3ysT7j + Ha3eJsO + LNS / omHDhsarP8Z2eThW876iKJCCc / mB76a6u1e4Id + 52K5lG ++ m8Pn4Gs + cqd2sKUKcMJ9CkJ6dBIdGlXHMoOHj4C33SPrEG / vEBv5cu0l5PP3RiBAuaZHpLKzfIiaLOpj / k4dD / weVt5gwTIJn16AEgPD7173Xef0HgoPlQInDFrJwsGpYCnIPZWSxRbvjKkya2Auj0QZyMCrXwIDAQAB
    -----结束公共密钥-----“”“

    access_token =“ eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI0LVlJOUlVc2R6NGM0SHoycXczT0xXZ0I0eHc2eFd4T29XdktVT2FvV3FzIn0。eyJqdGkiOiJiMGFhZDllMC1lYmU5LTQ4ZDQtYTcxNC1iMWMyNjg4NmM2MDciLCJleHAiOjE0ODE5ODE0ODQsIm5iZiI6MCwiaWF0IjoxNDgxODk1MDg0LCJpc3MiOiJodHRwczovL3BtaS1rZXljbG9hay5zYnZpbXByb3Zlci5jb20vYXV0aC9yZWFsbXMvT3BlbkJFTCIsImF1ZCI6ImJlbG1nciIsInN1YiI6ImU1ODc2OGQxLWU3ODktNGU3Yi04ZGVlLWJjMzYxNzFkZDNhZCIsInR5cCI6IkJlYXJlciIsImF6cCI6ImJlbG1nciIsIm5vbmNlIjoiMzI3NjRhYWUtZmM1NC00MDlkLTgxM2EtNjhhNmM4YTNhYzI2IiwiYXV0aF90aW1lIjoxNDgxODk1MDgzLCJzZXNzaW9uX3N0YXRlIjoiMWQwNDMyMDQtNWNkYy00OTVjLWJlZWUtODIwZWJiMmRlNWUzIiwiYWNyIjoiMSIsImNsaWVudF9zZXNzaW9uIjoiMmEzZWY1MzgtN2MxOS00YzE3LTlmZTctYjQ3ZGNjNmM0ODQyIiwiYWxsb3dlZC1vcmlnaW5zIjpbIioiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsInZpZXctcHJvZmlsZSJdfX0sIm5hbWUiOiJXaWxsaWFtIEhheWVzIiwicHJlZmVycmVkX3VzZXJuYW1lIjoid2lsbGlhbS5zLmhheWVzQGdtYWlsLmNvbSIsImdpdmVuX25hbWUiOiJXaWxsaWFtIiwiZmFtaWx5X25hbWUiOiJIYXllcyIsImVtYWlsIjoid2lsbGlhbS5zLmhheWVzQGdtYWlsLmNvbSJ9.DfC1c6BVBZ8Bgwu6CYGBsWp4T0dqltwAQ84E1Q0LdjFxvtVeDOF8rBIdgkr7rMCHObZWsEotljSR4BZzCvfDNmdk_25sedvi-ZHXTP0-nSeHczIXBstZ8p257A6-fEiIcG5CRoClHMI317bVGjNkzAV7l8kuBhr0bfrDedxpvKo3EQah4MrOF7-JXQGPAWlLDV1E9zsrT99Vm_XL58M-ur8q7N-B-CmOBV2GGsMEosTDK_-U-mattEN6PMNiG004Ryg0iPDM4-kr1AQsPE_wHBYf81_-vrqs7ec  -  0ShJYdC8-eBbuf9xVixNQVPRl7mnktaKA19YXdzdCwcQa6crw”DfC1c6BVBZ8Bgwu6CYGBsWp4T0dqltwAQ84E1Q0LdjFxvtVeDOF8rBIdgkr7rMCHObZWsEotljSR4BZzCvfDNmdk_25sedvi-ZHXTP0-nSeHczIXBstZ8p257A6-fEiIcG5CRoClHMI317bVGjNkzAV7l8kuBhr0bfrDedxpvKo3EQah4MrOF7-JXQGPAWlLDV1E9zsrT99Vm_XL58M-ur8q7N-B-CmOBV2GGsMEosTDK_-U-mattEN6PMNiG004Ryg0iPDM4-kr1AQsPE_wHBYf81_-vrqs7ec  -  0ShJYdC8-eBbuf9xVixNQVPRl7mnktaKA19YXdzdCwcQa6crw”DfC1c6BVBZ8Bgwu6CYGBsWp4T0dqltwAQ84E1Q0LdjFxvtVeDOF8rBIdgkr7rMCHObZWsEotljSR4BZzCvfDNmdk_25sedvi-ZHXTP0-nSeHczIXBstZ8p257A6-fEiIcG5CRoClHMI317bVGjNkzAV7l8kuBhr0bfrDedxpvKo3EQah4MrOF7-JXQGPAWlLDV1E9zsrT99Vm_XL58M-ur8q7N-B-CmOBV2GGsMEosTDK_-U-mattEN6PMNiG004Ryg0iPDM4-kr1AQsPE_wHBYf81_-vrqs7ec  -  0ShJYdC8-eBbuf9xVixNQVPRl7mnktaKA19YXdzdCwcQa6crw”

    access_token_json = jwt.decode(access_token,public_key,受众='belmgr')
    打印(access_token_json)


Chr*_*ris 5

我想补充一下这些知识,以防其他人陷入困境。

直接从 Keycloak 领域 -> 密钥 -> 公钥 -> (视图)复制的 public_key 值不起作用。相反,我必须使用以下 URL 获取 Keycloak 为领域公开的公钥:

https://<< my keycloak url >>/auth/realms/<< my realm >>/
Run Code Online (Sandbox Code Playgroud)

此 url 返回 json,其中包含领域“我的领域”的“public_key”值。按照所选答案中建议的方式使用公钥的此值。

但是,您还需要确保解码调用的受众价值正确。对于我的使用,值“belmgr”。我通过使用 jwt.io 找到了我需要的受众,解码了 access_token,并在有效负载数据中,我发现:

{ ..."aud": "account",... }
Run Code Online (Sandbox Code Playgroud)

使用“帐户”作为观众的工作。

# wrong audience - FAIL
access_token_json = jwt.decode(access_token, public_key, audience='belmgr')

# right audience - SUCCESS
access_token_json = jwt.decode(access_token, public_key, audience='account')
Run Code Online (Sandbox Code Playgroud)