java Keycloak CORS过滤器 Spring Boot

9cbw7uwe  于 2023-02-07  发布在  Java
关注(0)|答案(9)|浏览(170)

我正在使用keycloak来保护我的rest服务。我参考了here的教程。我创建了rest和前端。现在当我在后端添加keycloak时,我的前端调用API时会收到CORS错误。
Application.java Boot 中的www.example.com文件看起来像

@SpringBootApplication
public class Application 
{
    public static void main( String[] args )
    {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public WebMvcConfigurer corsConfiguration() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/api/*")
                        .allowedMethods(HttpMethod.GET.toString(), HttpMethod.POST.toString(),
                                HttpMethod.PUT.toString(), HttpMethod.DELETE.toString(), HttpMethod.OPTIONS.toString())
                        .allowedOrigins("*");
            }
        };
    }
}

www.example.com文件中的keycloak属性application.properties如下所示

keycloak.realm = demo
keycloak.auth-server-url = http://localhost:8080/auth
keycloak.ssl-required = external
keycloak.resource = tutorial-backend
keycloak.bearer-only = true
keycloak.credentials.secret = 123123-1231231-123123-1231
keycloak.cors = true
keycloak.securityConstraints[0].securityCollections[0].name = spring secured api
keycloak.securityConstraints[0].securityCollections[0].authRoles[0] = admin
keycloak.securityConstraints[0].securityCollections[0].authRoles[1] = user
keycloak.securityConstraints[0].securityCollections[0].patterns[0] = /api/*

我调用的示例REST API

@RestController
public class SampleController {    
    @RequestMapping(value ="/api/getSample",method=RequestMethod.GET)
    public string home() {
        return new string("demo");
    }        
}

前端keycloak.json属性包括

{
  "realm": "demo",
  "auth-server-url": "http://localhost:8080/auth",
  "ssl-required": "external",
  "resource": "tutorial-frontend",
  "public-client": true
}

我得到的CORS误差

XMLHttpRequest cannot load http://localhost:8090/api/getSample. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:9000' is therefore not allowed access. The response had HTTP status code 401.
r3i60tvu

r3i60tvu1#

我知道......这个问题很老了。但是如果你在使用Sping Boot + Keycloak进行本地开发时遇到了问题,你可以使用Config

keycloak.cors=true

在您的应用程序属性中。
干杯:)

gk7wooem

gk7wooem2#

尝试像我的例子一样创建CORSbean。我最近经历了同样的事情(让CORS工作),这是一场噩梦,因为SpringBoot CORS支持目前不像MVC CORS那样健壮或直接。

@Bean
public FilterRegistrationBean corsFilter() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    config.addAllowedOrigin("*");
    config.addAllowedHeader("*");
    config.addAllowedMethod("*");
    source.registerCorsConfiguration("/**", config);

    FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
    bean.setOrder(0);
    return bean;
}

这就是我如何设置它来接受应用程序范围内的任何源,但是如果你改变一些参数,你应该能够复制你想要的。也就是说,如果你只想添加你提到的方法,链接一些addAllowedMethod()。允许的源将是相同的,然后你的addMapping("/api/*")将成为source.registerCorsConfiguration("/api/*", config);
编辑:
Spring Data Rest and Cors
看看这个,Sebastian是Spring工程团队的一员,所以这是你能得到的最好的官方答案。

xmjla07d

xmjla07d3#

我来到这里有同样的问题,并修复它省略验证OPTIONS方法只,像这样:

keycloak.securityConstraints[0].security-collections[0].omitted-methods[0]=OPTIONS

它对我有效,因为OPTIONS请求Keycloack不包括身份验证头。

更新我的浏览器缓存有问题,因此我无法看到后端代码更改的真实的影响。看起来真正对我有效的是在@RestController级别启用所有CORS原点,如下所示:

@CrossOrigin(origins = "*")
@RestController
public class UsersApi {...}
5t7ly7z5

5t7ly7z54#

我无法访问代码示例,但根据您包含的代码配置,似乎缺少的配置导致spring排除CORS头。
J. West的回答与我最近在Spring和CORS中遇到的问题相似,但是我想提醒您查看Spring示例引用的是哪一个实现,因为有两个. Spring SecuritySpring MVC Annotations。这两个实现彼此独立工作,不能组合。
当你使用基于过滤器的方法时(即使是简单的),关键是设置allow credentials为true,以便浏览器跨域发送认证头。我也建议使用上面建议的完整代码方法,因为这将允许你创建一个更可配置的web应用程序,通过属性注入或服务注册表跨多个域或环境部署。

0g0grzrc

0g0grzrc5#

Access-Control-Allow-Origin头应该由服务器应用程序根据在对服务器应用程序的请求中提供的Origin请求头来设置。通常,浏览器在检测到正在进行跨源请求时设置请求中的Origin头。并且它们期望Access-Control-Allow-Origin头作为响应来允许它。
现在,对于keycloak,我也在为同样的问题而挣扎,看看this,似乎keycloak在错误响应的情况下不添加Access-Control-Allow-Origin头,然而,对我来说,即使在成功响应的情况下,它也不会在响应中添加这个头。
查看代码并添加断点,我注意到客户端对象的webOrigin不会从Origin标头填充,即使通过了,因此CORS不会添加访问控制响应标头。
我能够通过在CORS构建调用之前添加以下代码行来使其工作:

client.addWebOrigin(headers.getRequestHeader("Origin").get(0));

之前:

Cors.add(request, Response.ok(res, MediaType.APPLICATION_JSON_TYPE)).auth().allowedOrigins(client).allowedMethods("POST").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build();

一旦我构建了包含此更改的代码并启动了服务器,我就开始获得三个访问控制响应头:

Access-Control-Expose-Headers: Access-Control-Allow-Methods 
Access-Control-Allow-Origin: http://localhost:9000 
Access-Control-Allow-Credentials: true

我正在使用客户端凭据授予类型;因此,我只在www.example.com的buildClientCredentialsGrant中添加了它。TokenEndpoint.java#L473.
我仍然需要做一些更多的代码潜水,以便肯定地说,这是一个错误的成功响应以及找到一个更好的地方设置这对客户端对象在keycloak代码(如客户端对象正在构造)
欢迎你来试一试。

    • 更新日期:**

我收回这句话。我在keycloak中重新注册了我的客户端,根URL为http://localhost:9000(这是我的前端应用程序端口),我开始获得正确的访问控制响应头。希望这对你有帮助。

pgky5nke

pgky5nke6#

我知道这个问题太老了,但是我找到了更好的解决方案。Read more at official documentation
在应用程序.yml文件中

keycloak:
  auth-server-url: http://localhost:8180/auth
  realm: CollageERP
  resource: collage-erp-web
  public-client: true
  use-resource-role-mappings: true
  cors: true
  cors-max-age: 0
  principal-attribute: preferred_username
  cors-allowed-methods: POST, PUT, DELETE, GET
  cors-allowed-headers: X-Requested-With, Content-Type, Authorization, Origin, Accept, Access-Control-Request-Method, Access-Control-Request-Headers

也可以使用www.example.com文件进行配置application.properties

keycloak.auth-server-url= http://localhost:8180/auth
  keycloak.realm= CollageERP
  keycloak.resource= collage-erp-web
  keycloak.public-client= true
  keycloak.use-resource-role-mappings= true
  keycloak.cors= true
  keycloak.cors-max-age= 0
  keycloak.principal-attribute= preferred_username
  keycloak.cors-allowed-methods= POST, PUT, DELETE, GET
  keycloak.cors-allowed-headers= X-Requested-With, Content-Type, Authorization, Origin, Accept, Access-Control-Request-Method, Access-Control-Request-Headers

还有我的java适配器类

import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
import org.keycloak.adapters.springsecurity.KeycloakConfiguration;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.core.session.SessionRegistryImpl;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;

import javax.ws.rs.HttpMethod;

@KeycloakConfiguration
@EnableGlobalMethodSecurity(jsr250Enabled = true)
public class KeycloakSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.cors().and().authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS).permitAll()
                .antMatchers("/api/**")
                .authenticated()
                .anyRequest().permitAll();
        http.csrf().disable();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(keycloakAuthenticationProvider());
    }

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

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

}
uxh89sit

uxh89sit7#

我想和你们分享我的解决方案,希望能帮助任何面临同样问题的人。我将给予你们两个解决方案。

Spring无功:

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
    @Autowired
    private ReactiveClientRegistrationRepository clientRegistrationRepository;

    @Bean
    SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {

        CorsConfiguration cors_config = new CorsConfiguration();
        cors_config.setAllowCredentials(true);
        cors_config.applyPermitDefaultValues();
        cors_config.setAllowedOrigins(Arrays.asList("http://localhost:3000", "null"));
        cors_config.setAllowedMethods(List.of("GET","POST","OPTIONS","DELETE"));
        cors_config.setAllowedHeaders(List.of("*"));

        http.cors().configurationSource(source -> cors_config)
                .and()
                .csrf().disable()
                .authorizeExchange(exchanges -> exchanges.anyExchange().authenticated())
                .oauth2Login()//Setting Oauth2Login
                .authenticationSuccessHandler(new RedirectServerAuthenticationSuccessHandler("")).and()
                .logout(logout -> logout //Setting Oauth2Logout
                        .logoutHandler(logoutHandler())
                        .logoutSuccessHandler(oidcLogoutSuccessHandler()));
        return http.build();
    }

    private ServerLogoutSuccessHandler oidcLogoutSuccessHandler() {
        OidcClientInitiatedServerLogoutSuccessHandler oidcLogoutSuccessHandler =
                new OidcClientInitiatedServerLogoutSuccessHandler(this.clientRegistrationRepository);
        // Sets the location that the End-User's User Agent will be redirected to
        // after the logout has been performed at the Provider
        oidcLogoutSuccessHandler.setPostLogoutRedirectUri("");
        return oidcLogoutSuccessHandler;
    }

    private DelegatingServerLogoutHandler logoutHandler() {
        //Invalidate session on logout
        return new DelegatingServerLogoutHandler(
                new SecurityContextServerLogoutHandler(), new WebSessionServerLogoutHandler());
    }

}

** Spring MVC:**

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        CorsConfiguration cors_config = new CorsConfiguration();
        cors_config.setAllowCredentials(true);
        cors_config.applyPermitDefaultValues();
        cors_config.setAllowedOrigins(Arrays.asList("http://localhost:3000", "null"));
        cors_config.setAllowedMethods(List.of("GET","POST","OPTIONS","DELETE"));
        cors_config.setAllowedHeaders(List.of("*"));
        http.cors().configurationSource(source -> cors_config).and()...
        

        return http.build();
    }

}

确保在Keycloak上也启用了cors,导航到realm->clients->settings->weborigins并提交您允许的来源。
如果您要在请求中发送凭据或Cookie,请确保对其进行配置,例如,如果您使用ReactJS:

const httpConfig = { withCredentials: true };

axios.get('YourUrl', httpConfig)
        .then(response => {})
        .catch(error => {})
        .finally(() => {});
xdyibdwo

xdyibdwo8#

当客户端发送身份验证标头时,不能使用allowedOrigins(“*”)。必须配置特定的源URL。

oknrviil

oknrviil9#

由于您已在www.example.com文件中设置了属性keycloak.cors = trueapplication.properties,因此必须在Keycloak服务器中提及启用CORS的原点。要执行此操作,请执行以下步骤。
转到Clients -> Select the client (Token owner) -> Settings -> Web Origins逐个添加源或添加 * 以允许所有。完成此操作后,您必须获得一个新令牌。(如果您解码令牌,您将看到您的源为allowed-origins": ["*"]
设置属性keycloak.cors = false是另一个选项,但这会完全禁用CORS。

相关问题