为何两种cURL获取access token请求一有效一失败?
这两种请求完全不等价,问题的核心在于请求参数的编码格式与服务端的预期不匹配!
1. 第一个请求为什么能正常工作?
你第一个用--data(或-d)传递参数的cURL命令,默认会把参数编码为application/x-www-form-urlencoded格式(也就是网页表单提交的标准格式)。而从请求URL能看出是Keycloak的OpenID Connect token端点,它对client_credentials授权类型的默认处理逻辑,就是只接受这种表单编码的参数传递方式,所以服务端能正确识别grant_type、client_id和client_secret这些参数,顺利返回Access Token。
另外补充个小细节:你第一个命令里的&其实可以直接写成&,在终端环境下完全没问题,简化后的命令是:
curl --data "grant_type=client_credentials&client_id=synchronization_tool&client_secret=8f6a6e73-66ca-4f8f-1234-ab909147f1cf" http://localhost:8080/auth/realms/master/protocol/openid-connect/token
2. 第二个请求为什么失败?
第二个请求你用了JSON格式传递参数,还设置了Content-Type: application/json,但Keycloak默认不支持用JSON来传递client_credentials授权的参数。服务端依然在等待表单编码的参数,自然找不到grant_type这个表单参数,所以返回了invalid_request错误。
虽然OAuth 2.0规范没有强制要求必须使用表单编码,但绝大多数服务端实现(包括Keycloak)都只支持表单编码的参数传递方式来处理客户端凭证授权。
解决方案
如果你想坚持用JSON格式请求,需要修改Keycloak的配置——在对应Realm的客户端设置里,开启对JSON请求体的支持。不过更简单、通用的方式是直接使用第一个请求的表单编码格式,这是最不容易出问题的做法。
内容的提问来源于stack exchange,提问作者dafero

