首页 > 解决方案 > Spring Boot Keycloak Adapter 无法授权对上下文 url“/”的请求

问题描述

我有一个简单的 Spring Boot (2.4.5-SNAPSHOT) Web 项目,使用keycloak-spring-boot-starter(12.0.4) 适配器进行 Keycloak 集成。我可以保护除应用程序的上下文/基本 url 之外的所有端点。到达此基本 url 的请求未经过身份验证。configure我在方法上犯了错误吗?

http://localhost:3000/greetings是安全的,重定向到 Keycloak 登录。但是http://localhost:3000很不安全。

HelloController.java

@RestController
public class HelloController {

    @GetMapping("/greetings")
    public ResponseEntity<String> getGreetings() {
        return ResponseEntity.ok("Hello world!");
    }

    @GetMapping("/")
    public ResponseEntity<String> getContextGreetings() {
        return ResponseEntity.ok("Hello world context!");
    }
}

KeycloakSecurityConfig.java

@Configuration
@EnableWebSecurity
public class KeycloakSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.csrf().disable()
            .authorizeRequests().antMatchers("/**").authenticated();
        //.authorizeRequests(authorize -> authorize.anyRequest().authenticated());
        // Also tried the commented one, doesn't work either.
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) {
        KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakAuthenticationProvider);
    }

    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Bean
    public KeycloakConfigResolver keycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }
}

application.properties

server.port=3000

keycloak.realm=myrealm
keycloak.auth-server-url=http://localhost:8080/auth
keycloak.ssl-required=external
keycloak.resource=my-client
keycloak.credentials.secret=b5c3154c-012b-4ce2-af14-d58505a2a54d
keycloak.use-resource-role-mappings=true

构建.gradle

...
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
    providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.springframework.security:spring-security-test'
    implementation 'org.keycloak:keycloak-spring-boot-starter:12.0.4'
    implementation 'org.keycloak.bom:keycloak-adapter-bom:12.0.4'
    implementation 'com.fasterxml.jackson.core:jackson-core:2.12.2'
}

标签: javaspring-bootspring-securitykeycloak

解决方案


由于以下SO answer找到了原因。

configure方法中,super.configure(http);被调用。父configure方法使注销 url 不安全,这是有道理的,因为它是注销后的重定向页面。为了克服这个问题,logoutSuccessUrl需要设置为另一个 url。

请参阅方法的最后一行KeycloakWebSecurityConfigurerAdapter

    protected void configure(HttpSecurity http) throws Exception {

        http
                .csrf().requireCsrfProtectionMatcher(keycloakCsrfRequestMatcher())
                .and()
                .sessionManagement()
                .sessionAuthenticationStrategy(sessionAuthenticationStrategy())
                .and()
                .addFilterBefore(keycloakPreAuthActionsFilter(), LogoutFilter.class)
                .addFilterBefore(keycloakAuthenticationProcessingFilter(), LogoutFilter.class)
                .addFilterAfter(keycloakSecurityContextRequestFilter(), SecurityContextHolderAwareRequestFilter.class)
                .addFilterAfter(keycloakAuthenticatedActionsRequestFilter(), KeycloakSecurityContextRequestFilter.class)
                .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint())
                .and()
                .logout()
                .addLogoutHandler(keycloakLogoutHandler())
                .logoutUrl("/sso/logout").permitAll()
                .logoutSuccessUrl("/"); //permits the path
    }

推荐阅读