在ocp开源项目auth-sso测试的过程中,因为Redis client details数据的问题出现了几种错误。刚开始子涵先生也是摸不到头脑,经过艰苦卓绝的debug,最终定位到是Redis缓存不当导致的。长舒一口气~
上一篇:《实战开源——ocp:SpringCloudAlibaba应该这样学!》
单点登录测试用例
访问地址:http://127.0.0.1:9997/dashboard/
本测试中我们选取了下面的三个场景:
- 锁定应用,不允许单点功能;
- 解锁应用,单点功能应恢复;
- 修改应用信息,单点功能应正常使用。
不巧的是,三个测试场景除了第一个,其他场景均存在问题。作者开源不易,在此本渣还是要给出10万分的感谢。
测试错误总结
测试过程果然没那么顺利,此处来一个错误的小总结吧。
- 错误一:
AuthorizationEndpoint Handling ClientRegistrationException error: No client with requested id: owen
;(redis缺少该client造成的) - 错误二:
org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder Empty encoded password
;(Redis数据中缺少client secret造成的) - 错误三:Redis中的Client Secret与数据库中的Secret不一致导致的;(源代码没有更新Redis缓存中的数据)。
测试现场
1、锁定应用
锁定应用“owen”。
访问sso主页,auth-server返回:
[auth-server:169.254.80.80:8000] [f55fce2e23409855,f55fce2e23409855] 2020-12-15 11:20:53.685 ERROR 15308 [http-nio-8000-exec-8] com.open.capacity.uaa.server.service.RedisClientDetailsService clientId:owen,owen
[auth-server:169.254.80.80:8000] [f55fce2e23409855,f55fce2e23409855] 2020-12-15 11:20:53.685 ERROR 15308 [http-nio-8000-exec-8] com.open.capacity.uaa.server.service.RedisClientDetailsService clientId:owen,owen
[auth-server:169.254.80.80:8000] [f55fce2e23409855,f55fce2e23409855] 2020-12-15 11:20:53.686 INFO 15308 [http-nio-8000-exec-8] org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint Handling OAuth2 error: error="invalid_client", error_description="应用获取失败"
浏览器返回:
OAuth Error
error=“unsupported_response_type”, error_description="???", code=“400”, msg="???"
该日志信息是由org.springframework.security.oauth2.common.exceptions.OAuth2Exception返回的。
2、解除client锁定
解除client锁定,client端无法登录,auth-server报错:
[auth-server:169.254.80.80:8000] [7af92e2ba3037cf2,7af92e2ba3037cf2] 2020-12-15 14:03:59.606 WARN 18032 [http-nio-8000-exec-9]
org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder Empty encoded password
解决这个问题花费了很久的时间。最终的解决办法是:从官网理论看起。
最终原因:ocp代码存在bug,应用解锁后Redis中Client Details没有client secret导致的。
当我再次启用该应用时,相关的代码更新了Redis中的oauth_client_details,但是取值不正确,没有保存clientSecret进去,这也是造成修改应用后无法sso的根本原因;
相关代码如下:
@Override
public Result updateEnabled(Map<String, Object> params) {
try {
Long id = MapUtils.getLong(params, "id");
Boolean enabled = MapUtils.getBoolean(params, "status");
SysClient client = sysClientDao.getById(id);
if (client == null) {
return Result.failed("应用不存在");
//throw new IllegalArgumentException("用户不存在");
}
client.setStatus(enabled);
int i = sysClientDao.updateByPrimaryKey(client) ;
//出现问题的行在这里
ClientDetails clientDetails = client.map();
if(enabled){
redisTemplate.boundHashOps(UaaConstant.CACHE_CLIENT_KEY).put(client.getClientId(), JSONObject.toJSONString(clientDetails));
}else{
redisTemplate.boundHashOps(UaaConstant.CACHE_CLIENT_KEY).delete(client.getClientId()) ;
}
log.info("应用状态修改:{}", client);
return i > 0 ? Result.succeed(client, "更新成功") : Result.failed("更新失败");
} catch (InvalidClientException e) {
throw new ServiceException(e);
}
}
修复代码:
com.open.capacity.common.auth.details.DefaultClientDetails
map()中添加下方代码即可修复。
defaultClientDetails.setClientSecret(clientSecret);
3、SysClientService存在的问题
另外,在ocp的back-center中我们可以对OAuth2的client进行管理,在测试的过程中发现SysClientService在执行saveOrUpdate()
的时候没能把修改或新增的client信息更新到Redis中。修复代码如下:
@Override
public Result saveOrUpdate(SysClient sysClient) {
try {
sysClient.setClientSecret(passwordEncoder.encode(sysClient.getClientSecretStr()));
if (sysClient.getId() != null) {// 修改
sysClientDao.updateByPrimaryKey(sysClient);
} else {// 新增
SysClient r = sysClientDao.getClient(sysClient.getClientId());
if (r != null) {
return Result.failed(sysClient.getClientId()+"已存在");
}
sysClientDao.save(sysClient);
}
//【dwlijx】更新client 密码后应该同时刷新Redis中的缓存数据,否则会出现无法认证的问题
log.info("更新redis中的缓存数据");
redisTemplate.boundHashOps(UaaConstant.CACHE_CLIENT_KEY).put(sysClient.getClientId(), JSONObject.toJSONString(sysClient.map()));
return Result.succeed("操作成功");
} catch (Exception e) {
throw new ServiceException(e);
}
}
修复完成后,看到如下界面就OK了!
调试断点记录
matches:112, BCryptPasswordEncoder (org.springframework.security.crypto.bcrypt)
matches:592, WebSecurityConfigurerAdapter$LazyPasswordEncoder (org.springframework.security.config.annotation.web.configuration)
additionalAuthenticationChecks:90, DaoAuthenticationProvider (org.springframework.security.authentication.dao)
authenticate:166, AbstractUserDetailsAuthenticationProvider (org.springframework.security.authentication.dao)
authenticate:175, ProviderManager (org.springframework.security.authentication)