Spring Boot 和 Keycloak - 仅适用于 get 方法

Posted

技术标签:

【中文标题】Spring Boot 和 Keycloak - 仅适用于 get 方法【英文标题】:Spring Boot & Keycloak - only working for get method 【发布时间】:2019-01-27 20:25:35 【问题描述】:

以下工作正常:

@GetMapping(path = "/onlyforAdmins")
    @Secured("ROLE_ADMIN")
    public ResponseEntity<?> secureHello(Principal principal)  
        return new ResponseEntity<String>("hello " + principal.getName(), HttpStatus.OK);
    

但是,当我尝试以下操作时,我总是得到 403:

@RequestMapping(path = "/setupCase", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method=RequestMethod.POST)
    @Secured("ROLE_ADMIN")
    public MyCase setupCase(@RequestParam(required = false) String loggedInUser) throws Exception 

当我将方法切换到method=RequestMethod.GET 时,一切正常。

我有以下配置:

import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
import org.keycloak.adapters.springsecurity.KeycloakSecurityComponents;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Profile;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer.AuthorizedUrl;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;

@ConditionalOnClass(
    name = "org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter"
)
@ConditionalOnProperty(
    value = "keycloak.enabled",
    matchIfMissing = true
)
@EnableWebSecurity
@EnableGlobalMethodSecurity(
    securedEnabled = true
)
@ComponentScan(
    basePackageClasses = KeycloakSecurityComponents.class
)
public class MockWebSecurityConfigurerAdapter extends KeycloakWebSecurityConfigurerAdapter 
    public MockWebSecurityConfigurerAdapter() 
    

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception 
        KeycloakAuthenticationProvider keycloakAuthenticationProvider = this.keycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakAuthenticationProvider);
    


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

    protected void configure(HttpSecurity http) throws Exception 
        super.configure(http);
        ((AuthorizedUrl)((AuthorizedUrl)((AuthorizedUrl)((AuthorizedUrl)http.authorizeRequests().antMatchers(new String[]"/togglz-console/*")).hasRole("ADMIN").antMatchers(new String[]"/swagger-ui.html")).hasRole("ADMIN").antMatchers(new String[]"/admin/*")).hasRole("ADMIN").anyRequest()).permitAll();
    

    @Bean
    public KeycloakSpringBootConfigResolver KeycloakConfigResolver() 
        return new KeycloakSpringBootConfigResolver();
    

还有以下依赖:

    <dependency>
                <groupId>org.keycloak</groupId>
                <artifactId>keycloak-spring-boot-starter</artifactId>
                <optional>true</optional>
            </dependency>

<dependency>
                <groupId>org.keycloak.bom</groupId>
                <artifactId>keycloak-adapter-bom</artifactId>
                <version>4.1.0.Final</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

那么如何使 post 方法与 get 方法相同?即使我使用角色管理员登录,也要避免永久 403。

更新

我启用了日志记录并看到以下内容:

 o.s.security.web.csrf.CsrfFilter         : Invalid CSRF token found for http://localhost:8092/setupCase

【问题讨论】:

【参考方案1】:

问题与默认的 spring CSRF 保护有关。

发布请求需要提供 CSRF 令牌。

将以下内容临时添加到配置中,使 403 消失:

http.csrf().disable();

【讨论】:

是否还有一个配置可以在启用 CSRF 的情况下解决这个问题?【参考方案2】:

遇到了同样的问题。我们的解决方案也是在 KeycloakConfiguration.java configure() 覆盖方法中包含 csrf().disable()。

@Override
protected void configure(HttpSecurity http) throws Exception 
  super.configure(http);

  http.csrf().disable()
        .authorizeRequests()
        .antMatchers(NO_AUTH_PATHS).permitAll()
        .anyRequest().authenticated();

【讨论】:

以上是关于Spring Boot 和 Keycloak - 仅适用于 get 方法的主要内容,如果未能解决你的问题,请参考以下文章

dockerized 环境中的 Keycloak 和 Spring Boot Web 应用程序

Docker(Spring Boot 或 Thorntail)和 Keycloak

Spring Boot 我无法切换 keycloak 和基本身份验证

403 spring-boot-2-keycloak-适配器

Spring Boot 和 Keycloak - 仅适用于 get 方法

Keycloak Spring Boot 适配器和匿名资源