一、keycloak的实现
keycloak中创建领域realm,在领域中可以创建很多客户端,如果是微服务项目,就是很多微服务和前端项目作为客户端。
keycloak在实现客户端之间访问的令牌时,可以生成与用户无关的token,这种token主要用于服务到服务(Service-to-Service, S2S)的认证和授权。Keycloak支持多种机制来生成和管理这些令牌,其中最常见的是使用OAuth2.0的客户端凭证(Client Credentials)授权流程。
1、流程
以下是keycloak实现客户端之间访问令牌的关键步骤和机制:
1.1 配置客户端
在Keycloak中,每个微服务或客户端应用都需要作为一个客户端(Client). 被注册到Keycloak服务器中。在注册时,需要配置客户端的访问类型(Access Type),对于需要获取无用户交互的令牌的客户端,通常将其设置为confidential(保密的),并启用服务账户(Service Account)。
1.2 使用客户端凭证授权流程
客户端凭证授权流程是OAuth2.0定义的一种授权方式,它允许客户端(即微服务)使用自己的凭证(如客户端ID和客户端密钥)来获取访问令牌,而无需用户参与。以下是该流程的一般步骤:
1. 请求令牌:客户端向Keycloak的令牌端点(/auth/realms/{realm}/protocal/openid-connect/token)发送POST请求,请求中包含客户端ID(client_id)、客户端密钥(client_secret)以及授权类型(grant_type, 对于客户端凭证授权流程,该值为client_credentials).
2. 接收令牌:如果请求成功,Keycloak将返回一个JSON响应,其中包含访问令牌(access_token)、令牌类型(token_type, 通常为Bearer)、令牌的有效期(expires_in)等信息。这个访问令牌就是客户端之间的访问的令牌,它与用户无关。
3. 使用令牌:客户端在后续对其他微服务的HTTP请求中,需要在请求头中包含这个访问令牌(通过Authorization)头部,格式为Bearer {access_token})。被调用的微服务将验证令牌的有效性,以决定是否允许访问。
1.3 令牌验证
被调用的微服务可以使用Keycloak提供的公钥来验证访问令牌的签名,以确保令牌是由Keycloak签发的,并且未被篡改。此外,微服务还可以检查令牌的过期时间和其他声明(claims), 以进一步控制对资源的访问。
总之,Keycloak通过OAuth2.0的客户端凭证授权流程来实现客户端之间访问的与用户无关的令牌。这种机制为微服务架构中的服务到服务认证和授权提供了一种安全、灵活的方式。
2、Serivce Accounts Enabled配置项说明
Keycloak中的"Service Accounts Enabled" 选项是用于启用或禁用服务账户(Service Accounts)的功能。服务账户是OAuth2.0 推荐的系统服务使用的账户,它们允许微服务或后台服务以自己的身份进行认证和授权,而无需用户直接参与。以下是关于“Service Accounts Enabled”的详细解释:
功能作用:
1.启用服务账户:当"Service Accounts Enabled"被设置为ON时,Keycloak将为该客户端(client)生成一个服务账户。这个服务账户将包括一个客户端ID和一个客户端密钥(Client Secret)。它们可以用于后续的OAuth2.0流程中,以获取访问令牌(Access Token)等认证信息。
2. 支持服务到服务的认证:启用服务账户后,微服务或后台服务可以使用这些凭据(客户端ID和客户端密钥)通过Keycloak的API进行相互认证和授权。这支持了服务到服务(Service-to-Service,S2S)的通信模式,使得微服务架构中的服务能够安全地交换数据和调用彼此的功能。
3.提高安全性:通过为服务分配独立的身份和凭据,Keycloak帮助提高了系统的安全性。服务账户的使用可以减少对共享密钥或用户凭证的依赖,从而降低凭据泄露的风险。
使用场景:
1.微服务架构:在微服务架构中,服务账户允许各个微服务以独立的身份进行相互认证和授权,从而确保服务之间的通信是安全且受信任的。
2.后台服务:对于需要定期执行后台任务(如数据同步、报告生成等)的服务,服务账户提供了一种无用户交互的认证方式。
3.自动化脚本:在需要自动化执行某些操作的场景中(如CI/CD流程中的自动化测试),服务账户可以确保这些操作以可靠且安全的方式进行。
注意事项:
1.密钥管理:客户端ID和客户端密钥是敏感信息,需要妥善保管,避免泄露给未经授权的第三方。
2.安全性更新:定期更新服务账户的凭据和Keycloak服务器的安全设置,以抵御潜在的安全风险。
3.权限控制:仔细配置服务账户的权限,确保它们只能访问必要的资源和执行必要的操作。
总之,“Service Accounts Enabled”是Keycloak中一个重要的功能选项,它允许开发者为微服务或后台服务启用服务账户功能,从而支持服务到服务的认证和授权。这一功能在微服务架构和自动化流程中尤为重要,有助于提高系统的安全性和灵活性。
3、实际操作
客户端向keycloak发送POST请求获取token
请求方式:
- HTTP方法: POST
- URL:
/auth/realms/{realm}/protocol/openid-connect/token
(其中{realm}
是Keycloak中的领域名称)
请求体:
请求体通常包含以下参数,这些参数需要以 application/x-www-fom-urlencoded格式发送:
- client_id: 客户端的ID,即希望获取token的服务的标识符。
- client_secret: 客户端的密钥,用于验证客户端的身份。对于某些类型的客户端(如confidential客户端),这是必须的。
- grant_type: 授权类型,对于服务间token的获取,通常使用client_credentials
- scope(可选): 请求的权限范围,用空格分隔。这取决于资源服务器定义了哪些作用域,以及客户端希望请求哪些作用域。
我本地用apipost工具发起请求,生成curl示例如下
curl --location --request POST --X POST 'http://192.168.71.133:8080/auth/realms/demo/protocol/openid-connect/token' \
--header 'User-Agent: Apipost client Runtime/+https://www.apipost.cn/' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data 'grant_type=client_credentials' \
--data 'client_id=mark' \
--data 'client_secret=3deabc87-6217-48dd-ad27-2c21d5450c01'
得到结果
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJNaDNCVU5nQzJIU0h1MlZLQ2NpZlktZG42Q2ZuNmxRaVcwbGdvQWJFZEl3In0.eyJqdGkiOiI1YWIzMzNlNC02NmQ2LTRjODYtODY4Yy0yN2IxODhiNmMzN2YiLCJleHAiOjE3MjI1Nzc1NjAsIm5iZiI6MCwiaWF0IjoxNzIyNTc3MjYwLCJpc3MiOiJodHRwOi8vMTkyLjE2OC43MS4xMzM6ODA4MC9hdXRoL3JlYWxtcy9kZW1vIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjUxYjYwZWQ4LTBmZGMtNDBhOC04ZGFjLTIwYmY2YzljNGFlZiIsInR5cCI6IkJlYXJlciIsImF6cCI6Im1hcmsiLCJhdXRoX3RpbWUiOjAsInNlc3Npb25fc3RhdGUiOiI3MTU2Y2VkNC0yZTEyLTQ0MmYtOTA4NC05YzhlOWE0ZmQxNDMiLCJhY3IiOiIxIiwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6InByb2ZpbGUgZW1haWwiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImNsaWVudElkIjoibWFyayIsImNsaWVudEhvc3QiOiIxOTIuMTY4LjcxLjEiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJzZXJ2aWNlLWFjY291bnQtbWFyayIsImNsaWVudEFkZHJlc3MiOiIxOTIuMTY4LjcxLjEifQ.agybH7eyamRnvoF75w-jTS-0ZwKW6FzZZkM9Nnv0bnjOmLQIwkerhEPV8u-e949K0cAsHx0iNjpUM1iPEl3lj-TQpZa1LOMTh9BQBR5fwIkb8TTivl8wbJXTv-IYSovCLq5g5jdRGldIVRkibUvOq6HniuE18dVSOcTKW8z2ccvFR4go3Yp9mSht51nzFMdfZ6o35da8LdTu8jH-kjZcNAzu_jUZGMopG4Hccd2iXBZEeDdNeyOIvg0hBchdrgd0Tho8FAh-lt_uYVsz-tCzRFYDYNzOBj5ALNtm6b7gHzOQ9KL1wuZd23qgCjOKytlawktl-ssmVC_L8Q3hWBrr5A",
"expires_in": 300,
"refresh_expires_in": 1800,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJiMThhNjY5Mi1hZWFjLTRjY2MtYmEwNS01ZTg1ZDNkZTA0NTAifQ.eyJqdGkiOiJkOTdiOWY1Yy0xYmY3LTQ3ZmYtYTkwYS05YjEzNTQ1MjZkNjgiLCJleHAiOjE3MjI1NzkwNjAsIm5iZiI6MCwiaWF0IjoxNzIyNTc3MjYwLCJpc3MiOiJodHRwOi8vMTkyLjE2OC43MS4xMzM6ODA4MC9hdXRoL3JlYWxtcy9kZW1vIiwiYXVkIjoiaHR0cDovLzE5Mi4xNjguNzEuMTMzOjgwODAvYXV0aC9yZWFsbXMvZGVtbyIsInN1YiI6IjUxYjYwZWQ4LTBmZGMtNDBhOC04ZGFjLTIwYmY2YzljNGFlZiIsInR5cCI6IlJlZnJlc2giLCJhenAiOiJtYXJrIiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiNzE1NmNlZDQtMmUxMi00NDJmLTkwODQtOWM4ZTlhNGZkMTQzIiwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6InByb2ZpbGUgZW1haWwifQ.5jJy0KxJnCov52HJ226WHkKave5JqIfbdQG82sFTvos",
"token_type": "bearer",
"not-before-policy": 0,
"session_state": "7156ced4-2e12-442f-9084-9c8e9a4fd143",
"scope": "profile email"
}
access_token是一个jwt的格式,解析出明文内容如下:
eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJNaDNCVU5nQzJIU0h1MlZLQ2NpZlktZG42Q2ZuNmxRaVcwbGdvQWJFZEl3In0
{"jti":"5ab333e4-66d6-4c86-868c-27b188b6c37f","exp":1722577560,"nbf":0,"iat":1722577260,"iss":"http://192.168.71.133:8080/auth/realms/demo","aud":"account","sub":"51b60ed8-0fdc-40a8-8dac-20bf6c9c4aef","typ":"Bearer","azp":"mark","auth_time":0,"session_state":"7156ced4-2e12-442f-9084-9c8e9a4fd143","acr":"1","realm_access":{"roles":["offline_access","uma_authorization"]},"resource_access":{"account":{"roles":["manage-account","manage-account-links","view-profile"]}},"scope":"profile email","email_verified":false,"clientId":"mark","clientHost":"192.168.71.1","preferred_username":"service-account-mark","clientAddress":"192.168.71.1"}
上面jwt的token解析开来,可以看到preferred_username字段值是service-account-mark, 这就是把mark客户端作为了一个服务账户,和其他服务通信时,能识别身份。