Keycloak OAuth2出现[authorization_request_not_found]错误求助
在微服务架构(包含auth-service和gateway)中,通过docker-compose部署所有服务及Keycloak,集成Keycloak实现OAuth2认证时,页面抛出[authorization_request_not_found]错误。
一、环境配置
1. Docker-Compose配置
gateway-server: image: 'gateway-server:1.0' container_name: gateway ports: - "8080:8080" restart: on-failure environment: - SPRING_PROFILES_ACTIVE=prod - EUREKA_CLIENT_SERVICEURL_DEFAULTZONE=http://eureka-server:8761/eureka depends_on: - eureka-server networks: - mynet auth-server: image: 'auth-service:1.0' container_name: auth-server ports: - "8081:8081" restart: on-failure environment: - SPRING_PROFILES_ACTIVE=prod - EUREKA_CLIENT_SERVICEURL_DEFAULTZONE=http://eureka-server:8761/eureka - SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/mydb - SPRING_DATASOURCE_USERNAME=username - SPRING_DATASOURCE_PASSWORD=password depends_on: - eureka-server - db - keycloak-service networks: - mynet keycloak-service: image: 'keycloak/keycloak:latest' container_name: keycloak-service environment: - KEYCLOAK_ADMIN=admin - KEYCLOAK_ADMIN_PASSWORD=password ports: - "8085:8085" command: - start-dev - --http-port=8085 restart: always networks: mynet: aliases: - keycloak-service:8085 networks: mynet:
2. Hosts文件配置
127.0.0.1 localhost 127.0.0.1 keycloak-service
3. Keycloak配置类
@Configuration @Getter @Setter @RequiredArgsConstructor @ConfigurationProperties("keycloak") public class KeycloakConfig { private String serverUrl; private String realm; private String clientId; private String username; private String password; private String roleBuyer; private String roleAdmin; private String secret; @Bean public Keycloak keycloak() { ResteasyClientBuilder resteasyClientBuilder = new ResteasyClientBuilderImpl(); Keycloak keycloak = KeycloakBuilder.builder() .serverUrl(serverUrl) .grantType(OAuth2Constants.PASSWORD) .realm("master") .clientId("admin-cli") .username(username) .password(password) .resteasyClient(resteasyClientBuilder.connectionPoolSize(10).build()) .build(); RealmRepresentation newRealm = new RealmRepresentation(); newRealm.setId(realm); newRealm.setRealm(realm); newRealm.setEnabled(true); keycloak.realms().create(newRealm); ClientRepresentation clientRepresentation = new ClientRepresentation(); clientRepresentation.setClientId(clientId); clientRepresentation.setRedirectUris(List.of("http://127.0.0.1:8080/auth-service/*")); clientRepresentation.setWebOrigins(List.of("http://127.0.0.1:8080/auth-service")); clientRepresentation.setSecret(secret); keycloak.realm(realm).clients().create(clientRepresentation); RoleRepresentation role1 = new RoleRepresentation(); role1.setId(roleBuyer); role1.setName(roleBuyer); role1.setClientRole(true); RoleRepresentation role2 = new RoleRepresentation(); role2.setId(roleAdmin); role2.setName(roleAdmin); role2.setClientRole(true); keycloak.realm(realm).roles().create(role1); keycloak.realm(realm).roles().create(role2); return keycloak; } }
4. 安全配置类
@Configuration @EnableWebSecurity @Getter@Setter public class MyConfig { @Value("${keycloak.secret}") private String secret; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { httpSecurity.csrf().disable(); httpSecurity.authorizeHttpRequests() .anyRequest().authenticated() .and() .oauth2Login(); httpSecurity.oauth2Client(); return httpSecurity.build(); } @Bean public ClientRegistrationRepository clientRepository() { ClientRegistration keycloak = keycloakClientRegistration(); return new InMemoryClientRegistrationRepository(keycloak); } private ClientRegistration keycloakClientRegistration() { return ClientRegistration.withRegistrationId("teamgeist") .clientId("teamgeist-backend") .clientSecret(secret) .scope("openid") .redirectUri("http://127.0.0.1:8080/auth-service/login/oauth2/code/teamgeist-backend") .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .issuerUri("http://keycloak-service:8085/realms/teamgeist") .authorizationUri("http://keycloak-service:8085/realms/teamgeist/protocol/openid-connect/auth") .tokenUri("http://keycloak-service:8085/realms/teamgeist/protocol/openid-connect/token") .userInfoUri("http://keycloak-service:8085/realms/teamgeist/protocol/openid-connect/userinfo") .build(); } }
5. application.properties配置
spring.security.oauth2.client.registration.keycloak.client-id=teamgeist-backend spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code spring.security.oauth2.client.registration.keycloak.scope=openid spring.security.oauth2.client.provider.keycloak.issuer-uri=http://keycloak-service:8085/realms/teamgeist spring.security.oauth2.client.registration.keycloak.client-secret=${keycloak.secret} spring.security.oauth2.client.registration.keycloak.redirect-uri=http://127.0.0.1:8080/auth-service/login/oauth2/code/teamgeist-backend
排查与解决方案
1. 统一重定向URI配置
authorization_request_not_found核心原因是授权请求会话不匹配,重点检查重定向URI一致性:
- 修改KeycloakConfig中的客户端重定向URI,确保覆盖Spring的回调路径:
clientRepresentation.setRedirectUris(List.of("http://127.0.0.1:8080/auth-service/login/oauth2/code/*")); - 保持Spring配置和Keycloak客户端配置的URI完全一致,避免
localhost和127.0.0.1混用,统一使用同一格式。
2. 修正Keycloak外部访问地址
容器内部域名keycloak-service无法被浏览器解析,需将所有Keycloak相关配置替换为外部可访问地址:
- 安全配置类中,将
issuerUri、authorizationUri、tokenUri、userInfoUri中的keycloak-service替换为127.0.0.1 - application.properties中的
issuer-uri修改为http://127.0.0.1:8085/realms/teamgeist - KeycloakConfig中的
serverUrl设置为http://127.0.0.1:8085
3. 解决会话丢失问题
由于网关转发,auth-service的授权会话可能丢失:
- 配置全局会话存储(如Redis),确保auth-service的会话在网关转发后仍可访问。
4. 检查Keycloak客户端类型
确认Keycloak客户端的Access Type设置为confidential,authorization_code授权类型需要客户端密钥,必须开启此配置。
5. 避免重复创建Realm/客户端
当前KeycloakConfig每次启动都会重复创建Realm和客户端,可能导致配置冲突,添加判断逻辑:
// 仅在Realm不存在时创建 if (keycloak.realm(realm).find() == null) { keycloak.realms().create(newRealm); } // 仅在客户端不存在时创建 if (keycloak.realm(realm).clients().findByClientId(clientId).isEmpty()) { keycloak.realm(realm).clients().create(clientRepresentation); }
内容的提问来源于stack exchange,提问作者Sonnka
相关产品推荐
相关产品推荐

