Spring Boot 403不跳转Keycloak登录页及配置解析器异常排查
我正在搭建由独立Keycloak实例提供认证能力的Spring Boot API,所有服务运行在本地docker swarm/compose环境中。问题表现为:当引导用户访问被@RolesAllowed("ROLE_USER")注解拦截的/api/v3/login接口时,返回标准白标错误页,提示There was an unexpected error (type=Forbidden, status=403),我预期浏览器会跳转至Keycloak客户端登录页。
相关配置
应用基础配置
该配置用于从数据库而非配置文件中拉取Keycloak客户端配置,我们会根据用户Cookie中携带的邮箱域名匹配不同客户端:
@ComponentScan({"com.mycompany"}) @Configuration @EnableJpaRepositories(basePackages = "com.mycompany") @EntityScan("com.mycompany") public class ApplicationConfiguration { ... @Bean public KeycloakConfigResolver keycloakConfigResolver() { return new CustomKeycloakConfigResolver(); } }
自定义Keycloak配置解析器
public class CustomKeycloakConfigResolver implements KeycloakConfigResolver { @Autowired private KeycloakConfigService keycloakConfigService; ... @Override @Transactional public KeycloakDeployment resolve(final HttpFacade.Request request) { HttpFacade.Cookie cookie = request.getCookie("authDomain"); if (cookie == null) { return generateNullDeployment(); } final Pageable defaultPaging = PageRequest.of(0,1,Sort.by("id").ascending()); Page<KeycloakConfig> page = keycloakConfigService.readConfigsByFilter( "domain", cookie.getValue(), defaultPaging ); if ((page == null) || (page.getContent().size() < 1)) { return generateNullDeployment(); } KeycloakConfig config = page.getContent().get(0); AdapterConfig adapterConfig = new AdapterConfig(); adapterConfig.setRealm(config.getRealm()); adapterConfig.setResource(config.getResource()); adapterConfig.setPublicClient(config.getIsPublic()); adapterConfig.setAuthServerUrl(config.getAuthServerUrl()); adapterConfig.setSslRequired( config.getIsSslRequired() ? "all" : "none" ); adapterConfig.setUseResourceRoleMappings( config.getUseResourceRoleMappings() ); adapterConfig.setTokenStore(config.getTokenStore()); adapterConfig.setBearerOnly(config.getBearerOnly()); KeycloakDeployment keycloakDeployment = KeycloakDeploymentBuilder.build(adapterConfig); LOGGER.info("Keycloak Deployment Realm: {}", keycloakDeployment.getRealm()); LOGGER.info("Keycloak Deployment Resource: {}", keycloakDeployment.getResourceName()); LOGGER.info("Keycloak Deployment URL: {}", keycloakDeployment.getAuthUrl()); return keycloakDeployment; }
注:以上配置运行正常,仅单次请求会触发该resolve方法调用数十次,运行日志如下:
... o.k.adapters.KeycloakConfigResolver : Keycloak Deployment Realm: SpringBootKeycloak o.k.adapters.KeycloakConfigResolver : Keycloak Deployment Resource: SpringBootKeycloak o.keycloak.adapters.KeycloakDeployment : Loaded URLs from http://auth-service:8080/auth/realms/SpringBootKeycloak/.well-known/openid-configuration ... o.k.adapters.KeycloakConfigResolver : Keycloak Deployment Realm: SpringBootKeycloak o.k.adapters.KeycloakConfigResolver : Keycloak Deployment Resource: SpringBootKeycloak o.keycloak.adapters.KeycloakDeployment : Loaded URLs from http://auth-service:8080/auth/realms/SpringBootKeycloak/.well-known/openid-configuration ...
最终可得到有效的KeycloakDeployment,认证登录URL为http://auth-service:8080/auth。
安全配置
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(jsr250Enabled = true) public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter { @Override protected void configure(final HttpSecurity http) throws Exception { super.configure(http); http .csrf().disable() .antMatcher("/**") .authorizeRequests(); ...
因此所有请求都被授权,API端点代码如下:
... @RolesAllowed("ROLE_USER") @GetMapping( value = "/login", produces = MediaType.APPLICATION_JSON_VALUE ) @ResponseBody public Map<String, String> login() { final Map<String, String> response = new HashMap<String, String>(); response.put("status", "OK"); return response; } ...
我仅想通过该接口判断用户是否完成认证。预期未认证用户访问该接口时跳转至Keycloak登录页,但实际仅返回403禁止访问白页。
我猜测原因是安全配置中使用了.authorizeRequests(),未认证用户会被分配“Anonymous”角色,但我始终找不到正确的配置组合,实现未认证用户访问登录端点时跳转至KeycloakDeployment对应登录页的效果。
排查更新
我已经解开部分谜题:
我类路径下有一个遗留的AuthenticationEntryPoint类:
@ControllerAdvice public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
即使我从未通过.authenticationEntryPoint()指定使用它,Spring Boot自动配置机制仍自动识别并加载了该类。
我完全禁用该类后,现在至少能从/api/v3/login跳转至/sso/login了,但是/sso/login路径不会触发CustomKeycloakConfigResolver,导致无法获取KeycloakDeployment,抛出如下异常:
rest-api_1 | 2021-12-02 21:59:20.871 WARN 12 --- [nio-8080-exec-5] o.keycloak.adapters.KeycloakDeployment : Failed to load URLs from null/realms/null/.well-known/openid-configuration rest-api_1 | rest-api_1 | java.lang.IllegalStateException: Target host is null rest-api_1 | at org.apache.http.util.Asserts.notNull(Asserts.java:52) ~[httpcore-4.4.14.jar!/:4.4.14]
内容的提问来源于stack exchange,提问作者DrTeeth

