keycloak的access_token解析 用于后端接口鉴权
keycloak 获取token
https://{}:{}/auth/realms/{realm}/protocol/openid-connect/token
{
"access_token": "...",
"expires_in": 300,
"refresh_expires_in": 1800,
"refresh_token": "...",
"token_type": "bearer",
"id_token": "...",
"not-before-policy": 0,
"session_state": "53959d20-c80b-4923-82b8-bd32cd31563c",
"scope": "openid profile email"
}
access_token: 认证token
expires_in:到期时间
refresh_expires_in:刷新token到期时间
获取access_token后 和bearer拼接到header头中
-authorization:Bearer {access_token}
access_token 解析
1.keycloak的token是3分钟刷新一次,不是长效的token
这里我们只需要拿到 access_token 即可
eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwsia2lkIiA6ICI2ZDViYzFETUljRi1xRWcwTkhnQ0U3a1NEaEV3UlNGb3FVakF2RXo0NVE0In0.eyJleHAiOjE2ODIzMDQ5MzUsImlhdCI6MTY4MjMwNDYzNSwiYXV0aF90aW1lIjoxNjgyMzA0NjMxLCJqdGkiOiI4ZGFiODI4OS01MDM0LjAtOWQ5Mi1mNjBkYzdmODUwZDkiLCJpc3MiOiJodHRwOi8vbHl0YXBpLmxvbmd5b3UuZ292LmNuOjg0NDMvYXV0aC9yZWFsbXMvbHl0IiwiYXVkIjpbImF1dGgtYXBpIiwiYWNjb3VudCJdLCJzdWIiOiIwNjZlN2NjZS05ZjI0LTQ1NzUtOTk3MC1hMmQzYjRkMTllZWEiJ0eXAiOiJCZWFyZXIiLCJhenAiOiJtYW4iLCJub25jZSI6ImYNjU5LTk5NzAtNDBkYi05NTI3LWYyYTlkMTZjMTExOCIsInNlc3Npb25fc3RhdGUiOiI1Mzk1OWQyMC1jODBiLTQ5MjMtODJiOC1iZDMyY2QzMTU2M2MiLCJhY3IiOiIwIiwiYWxsb3dlZC1vcmlnaW5zIjpbImh0dHBzOi8venh3eS5sb25neW91Lmdvdi5jbjo4MDk1IiwiaHR0cHM6Ly95dGgubHllZHV5dW4uY29tIiwiaHR0cHM6Ly9seWZ5ZHAubG9uZ3lvdS5nb3YuY246OTQ0MyIsImh0dHBzOi8vcXdnYy5sb25neW91Lmdvdi5jbiIsImh0dHA6Ly9seS1nYXRld2F5Lm1veW9uLmNuIiwiaHR0cDovLzELjIzNS4xNTQ6MTAwMjEiLCJodHRwczovL3pobG4ubG9uZ3lvdS5nb3YuY24iLCJodHRwczovL2x5Y3MuY2hpbmE5NTA1OS5jb20uY246MTY2NjYiLCJodHRwczovL2x5dGFwaS5sb25neW91Lmdvdi5jbiIsImh0dHBzOi8veWxqei5sb25neW91Lmdvdi5jbiIsImh0dHA6Ly9seWZ5ZHAubG9uZ3lvdS5nb3YuY246ODA4NSJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMsib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImF1dGgtYXZSI6Im9wZW5pZCBwcm9maWxlIGVtYWlsIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJsuYW1lIjoi5aiBIOaxqiIsInByZWZlcnJlZF91c2VybmFtZSI6Ijc4OTg1OTI3QGRnd29yay5jb20iLCJnaXZlbl9uYW1lIjoi5aiBIiwiZmFtaWx5X25hbWUiOiLmsaoiLCJlbWFpbCI6Ijc4OTg1OTI3QGRnd29yay5jbs20ifQ.dP1XA2IOYZUD7E-HmQ-F3FaJr4XAgcFEaARSWpGu2pgRuq7lRqlWStoCEUFNaimb5-513SNpZEPQ-1cYn2iJgQHjGfbHD_gIVI1hKmoGIRqNONHpqiF_ttixF93bCYWQbp-cF09Yu-pQeKtl6z28yPcjF7OsagvRpBStclgOSEgwWrL2OAcQkiBfy8IVBpF20mu7B4Swar0VppQ0Brv0cMJCpLqKRrvoOXgGLGZxMiBRHKEfpOiClq5WN3Gbx_QuSy_JvXVHR-RfSRrHIUJ0eJFyulFKEjMooEWJ6x0eXiPwvFOhtu9rOa6huXr44MRjQN_aKjcZOPwod0xdtHoY4w
直接去用 base64 解密是失败的,
可以用 . 号区分开三段 ,这三段分别代表着不同的信息
第一段解析之后是keycloak 里realm里key的设置
第二段解析之后是登录返回的用户信息及配置信息
{
"exp":1682304935,
"iat":1682304635,
"auth_time":1682304631,
"jti":"8d234sdf9-5434-4fb0-9d92-f60234v850d9",
"iss":"http://{}:8443/auth/realms/{realm}",
"aud":[
"auth-api",
"account"
],
"sub":"066e7cce-9f24-4575-9970-a2d3b4d19eea",
"typ":"Bearer",
"azp":"client_id",
"nonce":"f8a8ew659-9970-40db-9527-f2ade34c1118",
"session_state":"5395crd20-c80b-4923-82b8-bd32ad2d563c",
"acr":"0",
"allowed-origins":[
"https://{{}}:8095"
],
"realm_access":{
"roles":[
"offline_access",
"uma_authorization"
]
},
"resource_access":{
"client_id":{
"roles":[
"roles"
]
},
"client_id":{
"roles":[
"roles"
]
},
"client_id":{
"roles":[
"roles"
]
}
},
"scope":"openid profile email",
"email_verified":false,
"name":"test",
"preferred_username":"test",
"given_name":"test",
"family_name":"test",
"email":"test"
}
exp:到期时间 注意这个时间戳是秒为单位的
preferred_username:用户信息
代码实例
这里使用springboot自带的拦截器实现
package com.hzlh.handler;
/**
* 基于keycloke的access token 做接口的鉴权 token每分钟刷新一次
*/
@Component
public class KeycloakAuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String authorization = request.getHeader("authorization");
if (StrUtil.isEmpty(authorization)){
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,"Invalid authentication token. Please check your token and try again");
return false;
}
authorization=authorization.replace("Bearer ","");
String[] splitToken = authorization.split("\\.");
String decodedToken = new String(Base64.getUrlDecoder().decode(splitToken[1]), StandardCharsets.UTF_8);
JSONObject jsonObject = JSONObject.parseObject(decodedToken);
//获得用户信息 可做用户校验这边只简单判空
String preferred_username = jsonObject.getString("preferred_username");
if (StrUtil.isEmpty(preferred_username)){
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,"Invalid authentication token. Please check your token and try again");
return false;
}
//令牌过期时间
String expTime = jsonObject.getString("exp");
if (StrUtil.isEmpty(expTime)){
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,"Invalid authentication token. Please check your token and try again");
return false;
}
long l = System.currentTimeMillis()/1000;
if (l > Convert.toLong(expTime)){
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,"Invalid authentication token. Please check your token and try again");
return false;
}
return true;
}
}
- 拦截器注册
package com.hzlh.config;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new KeycloakAuthInterceptor())
.addPathPatterns(
//拦截请求
);
}
}
注:本文涉及的密钥都是参了假数据的所以不要尝试解析,以你的数据为准