keycloak中refresh_token的相关测试

refresh_token是什么

起手先向chatgpt提问refresh_token的作用,得到的回答如下:

  1. 刷新访问令牌:访问令牌通常具有短暂的有效期,一旦过期,它就不能用于进行进一步的身份验证和授权请求。在这种情况下,客户端可以使用refresh_token向身份验证服务器发起请求,以获取一个新的有效的访问令牌。通过刷新令牌,客户端可以持续地访问受保护的资源,而无需再次要求用户进行身份验证。
  2. 提高安全性:相对于长期有效的访问令牌,refresh_token的有效期通常更长,因此可以减少访问令牌的生命周期,从而减少了黑客获取有效访问令牌的机会。如果令牌被泄露或被不法分子获取,它的有效期相对较短,限制了潜在损失。
  3. 用户交互:当访问令牌过期时,客户端可以使用refresh_token向身份验证服务器发起请求,获取新的访问令牌。这个过程通常是无需用户交互的,用户不需要再次提供他们的凭据来获取新的令牌,因此提供了更好的用户体验。

用人话来解释下chatgpt回答了一些什么

刷新访问令牌

我们都知道access_token的作用,无论是前端直接请求还是后端转发请求,当你想访问受到jwt保护的资源的时候,都要带上可爱的access_token。
根据最佳实践,一般access_token有效时间设置为30分钟。那么在用户使用网站超过30分钟的时候,会因为access_token过期而无法请求受保护的资源。
在这种情况下,使用refresh_token来获取新的access_token就是一种比较合理的选择。

为什么不直接使用较长有效期的access_token呢?

这是我的第一个疑问。仔细思考一下这个问题的本质,就是access_token里到底包含了什么。
它附带了用户的信息,比如关键的userid/username,附带了这个client能请求此用户的哪种资源(也就是scope)。
那么这些信息是否一成不变呢?答案是否定的,比如极端的情况我希望对一个名为QueryInfo的client下发一个180天有效期的access_token。
用户A同意了client-QueryInfo访问自己信息的请求,那么QueryInfo就拿到了用户A授予它的180天有效期的jwt。
在第20天的时候,用户A反悔了,他在自己的个人授权界面那里取消了对QueryInfo的授权。
但是如果管理员设置了一个180天有效期的access_token,那么QueryInfo就可以持续的拿着这个access_token来访问用户A的信息。
因为jwt的技术设计原因,此时的取消授权是无意义的。
恰巧一个懂行的管理员设置了30分钟有效期的access_token,180天有效期的refresh_token,那么QueryInfo使用refresh_token来换取access_token的时候,就会被拒绝,因为用户A他已经取消了授权。

是否维持较长时间的keycloak的session来达到同样的效果

这是我的第二个疑问,如果保持了keycloak登录的session,那么用户access_token过期之后自然就可以跳转到keycloak登录页面,
并且因为keycloak的session已经存在,自然会通过redirect_uri跳转回原页面,实际对用户是也是无感知的。
简单思考之后,发现跳转本身就需要有个过程,对keycloak本身也是一种压力,就算是不需要用户在用户名密码界面停留,也至少会造成白屏,进度条走动等问题。
所以refresh_token不能简单的被替代

keycloak中哪些参数控制了refresh_token

本文基于16.1版本的keycloak
Clients->对应的client->settings->advanced Settings 这其中有三个选项比较关键

  • Access Token Lifespan (控制access_token的有效期)
  • Client Session Idle (控制refresh_token的有效期)
  • Client Session Max (控制refresh_token有效期的最大值 一般没有特殊需求跟上一行同值即可)
    这三个值都有一个全局变量的默认值,是在realms菜单中的tokens里管理
    如果没有在此client下设置过值,那么默认就跟随全局变量走
    其中有一个需要注意的点是,全局变量中还有一个SSO Session Idle
  • SSO Session Idle代表了keycloak登录态的session最大默认登录时常。
  • Client Session Idle不能超过SSO Session Idle,max也同理。那么记得要把realms里面也改成一个比较大的值。

观察一个授权码模式的token返回

我们先把test_login这个client的配置文件按照上文所述改为1天有效期的Access Token Lifespan,180天有效期的Client Session Idle

访问授权码模式demo加上F12打开控制台可以看到
keycloak-refresh-refreshtoken.jpg
refresh有效时间已经被设置为180天,说明配置生效

使用refresh_token

判断access_token是否超过有效期,如果超过有效期则尝试使用refresh_token获取新的access_token
实际的token很长,缩短了一些

请求如下:

1
2
3
4
5
curl --location --request POST 'https://api.tongji.edu.cn/v1/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=test_login' \
--data-urlencode 'refresh_token=eyJhbGciOiJIUzI1Ni**********************UwMGUifQ.eyJleHAiOjE3OGM3Y***************hN2ZlN2MifQ.NIag08UV6j********YtOY' \
--data-urlencode 'grant_type=refresh_token'

返回如下:

1
2
3
4
5
6
7
8
9
10
11
{
"access_token": "实际的真实access_token",
"expires_in": 86400,
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIwOG0wVG5rb3M0QWVyaVE2Q0hiNUt0TGNXbDN6R0MtS2ZPbG5UYjVqRTF3In0.eyJleHAiOjE2ODkxNTI0NTksImlhdCI6MTY4OTA2NjA1OSwiYXV0aF90aW1lIjoxNjg5MDY1NDM3LCJqdGkiOiJhMDk4YWZiOC1hZDA2LTQwYWMtOTJhYS1jNDcyYjljNWJhNjciLCJpc3MiOiJodHRwczovL2FwaS50b25namkuZWR1LmNuL2tleWNsb2FrL3JlYWxtcy9PcGVuUGxhdGZvcm0iLCJhdWQiOiJ0ZXN0X2xvZ2luIiwic3ViIjoiZTVkYWIxYzMtZTlkYy00MzU2LTk3YTYtMDc0Mjg3MjkwZDYzIiwidHlwIjoiSUQiLCJhenAiOiJ0ZXN0X2xvZ2luIiwibm9uY2UiOiIzMTEzMWYxZS05NTkwLTQyYjItOGJhYy1lNTNlM2RiYThlYzUiLCJzZXNzaW9uX3N0YXRlIjoiNzhjYzdmOWQtMDZmOS00OWJmLThlMDMtODc2M2QzYTdmZTdjIiwiYXRfaGFzaCI6Il9LNlQ0Mzhmb0g0UEdNc05FMjZwbGciLCJhY3IiOiIwIiwic2lkIjoiNzhjYzdmOWQtMDZmOS00OWJmLThlMDMtODc2M2QzYTdmZTdjIiwibmFtZSI6IjIwNjY2MDkzIOS6juaZqCIsInByZWZlcnJlZF91c2VybmFtZSI6IjIwNjY2MDkzIiwiZ2l2ZW5fbmFtZSI6IjIwNjY2MDkzIiwiZmFtaWx5X25hbWUiOiLkuo7mmagiLCJ1c2VyaWQiOiIyMDY2NjA5MyJ9.EP68TlPvflhHVxIr8yaBYjcmUyDClUJTaAOibWoMujFW7U7631UdvDu1Knr402GgxebbeRNJRMCEbjcq5AMQtOdocbdaOh4_a98bSiSktBrMquEGIq6LGWjMstsDirFfYcKV1uaGvq74E5DPBAUUxWjLWCFgpTC6Ge-DaevZyIiGBODteHqv7c5mQXskW_R8rPHirFOy8oJc085RIpgIp1xwIbp4IvFcLWKKpJqCXgCmPfNZRPYWWPMTuJ2rBSFFBRWGLxwGMZJBUC7M4Q8Lc5riXNGlUnHVk7mr0bdCBfMWWLQjg-wThIJt9Z200O4MGutPe4R4NDKA-DFOONeXmQ",
"not-before-policy": 0,
"refresh_expires_in": 15551378,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIxMDNjZjRkMi0yYjdmLTQxM2EtYjEwZC01NjJlOTQzZDUwMGUifQ.eyJleHAiOjE3MDQ2MTc0MzcsImlhdCI6MTY4OTA2NjA1OSwianRpIjoiM2YwYzI1MzYtNWE4YS00YzQ2LWI5YTItOGJkNzQyZGI5NGYwIiwiaXNzIjoiaHR0cHM6Ly9hcGkudG9uZ2ppLmVkdS5jbi9rZXljbG9hay9yZWFsbXMvT3BlblBsYXRmb3JtIiwiYXVkIjoiaHR0cHM6Ly9hcGkudG9uZ2ppLmVkdS5jbi9rZXljbG9hay9yZWFsbXMvT3BlblBsYXRmb3JtIiwic3ViIjoiZTVkYWIxYzMtZTlkYy00MzU2LTk3YTYtMDc0Mjg3MjkwZDYzIiwidHlwIjoiUmVmcmVzaCIsImF6cCI6InRlc3RfbG9naW4iLCJub25jZSI6IjMxMTMxZjFlLTk1OTAtNDJiMi04YmFjLWU1M2UzZGJhOGVjNSIsInNlc3Npb25fc3RhdGUiOiI3OGNjN2Y5ZC0wNmY5LTQ5YmYtOGUwMy04NzYzZDNhN2ZlN2MiLCJzY29wZSI6Im9wZW5pZCBkY191c2VyX3NpbmdsZV9pbmZvIHByb2ZpbGUiLCJzaWQiOiI3OGNjN2Y5ZC0wNmY5LTQ5YmYtOGUwMy04NzYzZDNhN2ZlN2MifQ.Z2tL5f-8_x5NrgQWvwqE6iqOu4L3sBMK1jrshrgRYuU",
"scope": "openid dc_user_single_info profile",
"session_state": "78cc7f9d-06f9-49bf-8e03-8763d3a7fe7c",
"token_type": "Bearer"
}

可以看到又是一个86400s也就是24h有效期的access_token