Keycloak自定义用户SPI获取Access Token异常求助
Keycloak自定义用户SPI获取Token异常问题排查与解决
问题现象
- 基于自定义用户SPI实现后,Keycloak管理控制台可正常查看SPI内的用户,功能无异常
- 通过API工具调用Token端点时,返回错误:
"error": "invalid_grant", "error_description": "Invalid user credentials" - 特殊异常:在管理控制台查看user1后,可正常获取user1的Token;查看user2后可获取user2的Token,但再次调用user1的Token端点时会触发上述错误
解决方案
1. 修复SPI的凭证验证逻辑
参考的demo中InMemoryUserStorageProvider的validateCredentials方法可能存在逻辑缺陷,仅依赖控制台加载的内存用户实例进行验证。需修改为直接从数据源获取用户凭证进行比对:
@Override public boolean validateCredentials(RealmModel realm, UserModel user, CredentialInput input) { if (!(input instanceof UserCredentialModel)) return false; UserCredentialModel cred = (UserCredentialModel) input; // 从自定义数据源(而非临时缓存)获取用户存储的密码 String storedPassword = fetchPasswordFromYourDataSource(user.getUsername()); return storedPassword != null && storedPassword.equals(cred.getValue()); }
2. 修正用户存储的缓存策略
demo的内存存储可能使用单例实例存储用户,导致后续加载的用户覆盖之前的实例。需改为用Map存储所有用户:
// 替换原有单个用户实例为Map private final Map<String, UserModel> userCache = new HashMap<>(); // 加载用户时存入缓存 private UserModel loadUserByUsername(String username) { if (!userCache.containsKey(username)) { // 从数据源加载用户并构建UserModel UserModel user = buildUserModelFromDataSource(username); userCache.put(username, user); } return userCache.get(username); }
3. 调整用户存储优先级
确保自定义SPI的优先级高于内置用户存储,避免Keycloak优先从内置存储查找用户:
- 进入对应Realm的「用户存储」页面
- 将自定义SPI的优先级设为最高(数值越小优先级越高)
4. 再次验证请求参数
确认Token请求的参数与格式:
- 使用
password授权模式,请求参数包含:grant_type=password、client_id、client_secret(保密客户端必填)、username、password - 请求URL格式:
http://{keycloak地址}/realms/{Realm名称}/protocol/openid-connect/token
内容的提问来源于stack exchange,提问作者Chukwunonso Izueke
相关产品推荐
相关产品推荐

