简体   繁体   English

使用 ReactJS 应用程序命中端点时,Keycloak 保护 Spring Boot 应用程序 CORS 错误

[英]Keycloak secured Spring Boot Application CORS error when hitting endpoint using ReactJS application

I have a ReactJS and Java Spring Boot applications, both secured by Keycloak 11.0.2.我有一个 ReactJS 和 Java Spring Boot 应用程序,它们都由 Keycloak 11.0.2 保护。

Keycloak is on port 8083, ReactJS on 3000 and Spring App is on 8085. If I try to use the configuration provided below, I'm not able to hit my endpoint and I'm getting CORS error. Keycloak 在端口 8083 上,ReactJS 在 3000 上,Spring App 在 8085 上。如果我尝试使用下面提供的配置,我将无法访问我的端点并且我收到 CORS 错误。

Firefox:火狐:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8083/auth/realms/sorcerer_realm/protocol/openid-connect/auth?response_type=code&client_id=event_sorcerer&redirect_uri=http%3A%2F%2Flocalhost%3A8085%2Fsso%2Flogin&state=f52216b1-c235-4328-a2f9-d8448c3bf886&login=true&scope=openid. (Reason: CORS request did not succeed).

Chrome and Microsoft Edge: Chrome 和 Microsoft Edge:

Access to XMLHttpRequest at 'http://localhost:8083/auth/realms/sorcerer_realm/protocol/openid-connect/auth?response_type=code&client_id=event_sorcerer&redirect_uri=http%3A%2F%2Flocalhost%3A8085%2Fsso%2Flogin&state=f57ffa9f-9679-4476-aa03-af86c3abb3c2&login=true&scope=openid' (redirected from 'http://localhost:8085/api/worker/create/product') from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.


xhr.js:184 GET http://localhost:8083/auth/realms/sorcerer_realm/protocol/openid-connect/auth?response_type=code&client_id=event_sorcerer&redirect_uri=http%3A%2F%2Flocalhost%3A8085%2Fsso%2Flogin&state=f57ffa9f-9679-4476-aa03-af86c3abb3c2&login=true&scope=openid net::ERR_FAILED

When I try to hit my endpoint using Postman, I'm able to hit it.当我尝试使用 Postman 访问我的端点时,我能够访问它。 Below is my Keycloak Web Security configuration.下面是我的 Keycloak 网络安全配置。 The configuration uses application.properties file to configure Keycloak adapter.配置使用 application.properties 文件来配置 Keycloak 适配器。 When I set .authorizeRequests().antMatchers("/**").permitAll() in the config, I'm also able to hit my endpoint from browser and Postman.当我在配置中设置.authorizeRequests().antMatchers("/**").permitAll()时,我还可以从浏览器和 Postman 访问我的端点。

@KeycloakConfiguration
public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder authBuilder) throws Exception {
        final KeycloakAuthenticationProvider authProvider = keycloakAuthenticationProvider();
        
        authProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        authBuilder.authenticationProvider(authProvider);
    }
    
    
    /**
     * Call superclass configure method and set the Keycloak configuration
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        
        http
            .csrf().disable()
            .cors()
            .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and().anonymous()
            //.and().authorizeRequests().antMatchers("/**").permitAll()         //Uncomment for requests to be allowed!
            .and().authorizeRequests().antMatchers("/api/admin/**").hasRole("ADMIN")
            .and().authorizeRequests().antMatchers("/api/manager/**").hasAnyRole("MANAGER")
            .and().authorizeRequests().antMatchers("/api/worker/**").hasRole("WORKER")
            .anyRequest().authenticated();
    }

    /**
     * Setup Auth Strategy. Don't add prefixes and suffixes to role strings
     */
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }
    
    /**
     * Don't use keycloak.json. Instead, use application.yml properties.
     * @return
     */
    @Bean
    public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }
}

Here is a part of application.properties that sets up Keycloak:这是设置 Keycloak 的 application.properties 的一部分:

spring:
  jersey:
    type: filter


security: 
    oauth2: 
      resourceserver: 
        jwt: 
          issuer-uri: http://localhost:8083/auth/realms/sorcerer_realm/protocol/openid-connect/token
          jwk-set-uri: http://localhost:8083/auth/realms/sorcerer_realm/protocol/openid-connect/certs 


keycloak:
  realm: sorcerer_realm
  auth-server-url: http://localhost:8083/auth/
  ssl-required: external
  resource: event_sorcerer
  verify-token-audience: true
  credentials:
    secret-jwt:
      secret: d84611c9-af79-423b-b12c-bfa7fec23e85
  use-resource-role-mappings: true
  confidential-port: 0

Here is my ReactJS application's Keycloak adapter setup:这是我的 ReactJS 应用程序的 Keycloak 适配器设置:

const keycloakConfig = {
    "clientId": "event_sorcerer_frontend",
    "realm": "sorcerer_realm",
    "auth-server-url": "http://localhost:8083/auth/",
    "url": "http://localhost:8083/auth",
    "ssl-required": "external",
    "resource": "event_sorcerer",
    "public-client": true,
    "verify-token-audience": true,
    "use-resource-role-mappings": true,
    "confidential-port": 0
};

const keycloak = new Keycloak(keycloakConfig);

const initKeycloak = (onSuccessCallback, onFailureCallback) => {
    let success = false;

    timeoutWrapper(() => {
        if(!success){
            onFailureCallback();
        }
    });

    keycloak.init({
        onLoad: 'check-sso',
        silentCheckSsoRedirectUri: window.location.origin + '/silent-check-sso.html',
        pkceMethod: 'S256',

    }).then((isAuthenticated) => {
        success = true;
        if(isAuthenticated) {
            onSuccessCallback();
        } else {
            login();
        }
    });
}

Here is how I send the request to server:这是我将请求发送到服务器的方式:

export const Request = {
    configureAxiosDefault: () => {
        axios.defaults.baseURL = axiosDefaultConfiguration.baseUrl;
    },

    create: (data, endpoint, callback, errorCallback, finalCallback) => {
        axios.post(serverEndpoint + endpoint, {
            data: data,
            headers: {
                Authorization: `Bearer ${UserService.getToken()}`
            }
        })
        .then(response => Utility.isEmpty(callback) ?  defaultCallback(response) : callback(response))
        .catch(response => Utility.isEmpty(errorCallback) ? defaultErrorCallback(response) : errorCallback(response))
        .finally(response => {
            if(!Utility.isEmpty(finalCallback)) {
                finalCallback(response);
            }
        });
    },
}

Here is my Keycloak configuration for frontend.这是我的前端 Keycloak 配置。 Backend is the same, except the Access Type is confidential and the Root/Base url are different (not 3000 but 8085):后端是相同的,除了访问类型是机密的并且根/基本 url 不同(不是 3000 而是 8085):

前端

Here is my CORS configuration bean:这是我的 CORS 配置 bean:

@Configuration
public class CORSConfiguration {
    /**
     * Setup CORS
     * @return
     */
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        
        config.setAllowCredentials(true);
        config.setAllowedOrigins(Arrays.asList("http://localhost:3000"));
        config.setAllowedMethods(Arrays.asList(CorsConfiguration.ALL));
        config.setAllowedHeaders(Arrays.asList(CorsConfiguration.ALL));
        config.setAllowCredentials(true);
        source.registerCorsConfiguration("/**", config);
        
        return source;
    }
}

And lastly, here is my endpoint.最后,这是我的终点。 URL resolves to api/worker/create/product URL 解析为api/worker/create/product

@RestController
@RequestMapping(ControllerEndpointsPrefix.WORKER + "/create")
public class CreationController {
    @Autowired
    private UserAgregate userAgregate;

    @PostMapping("/product")
    public boolean createProduct(@RequestBody CreateProductCommand command) {
        return true;
    }
}

I've managed to solve this.我已经设法解决了这个问题。 The problem wasn't on the server side, but on client side.问题不在服务器端,而是在客户端。

configureAxiosDefault: () => {
    axios.defaults.baseURL = axiosDefaultConfiguration.baseUrl;
    axios.defaults.headers.Authorization = `Bearer ${UserService.getToken()}`
},

create: (data, endpoint, callback, errorCallback, finalCallback) => {
        axios.post(serverEndpoint + endpoint, data)
        .then(response => Utility.isEmpty(callback) ?  defaultCallback(response) : callback(response))
        .catch(response => Utility.isEmpty(errorCallback) ? defaultErrorCallback(response) : errorCallback(response))
        .finally(response => {
            if(!Utility.isEmpty(finalCallback)) {
                finalCallback(response);
            }
        });
    },

Server was unable to process the token, because I was sending it as a JSON object property.服务器无法处理令牌,因为我将它作为 JSON 对象属性发送。 These changes made everything work OK.这些更改使一切正常。

So, CORS wasn't an issue at all.因此,CORS 根本不是问题。 The issue was that request didn't contain an Authorization header.问题是请求不包含授权标头。

There are a lot of StackOverflow questions regarding KeyCloak, and some of them incomplete and cryptic.有很多关于 KeyCloak 的 StackOverflow 问题,其中一些是不完整和神秘的。 I encountered a good amount of errors, because of OpenJDK, JDK versions etc. If anyone needs explanations and solutions, working Spring Boot configuration is on my repository: https://github.com/milosrs/EventSorcererBackend由于 OpenJDK、JDK 版本等原因,我遇到了大量错误。如果有人需要解释和解决方案,可以在我的存储库中找到有效的 Spring Boot 配置: https://github.com/milosrs/EventSorcererBackend : https://github.com/milosrs/EventSorcererBackend

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在开发Spring Boot和reactjs应用程序期间处理CORS - Handling CORS during development Spring Boot and reactjs application Keycloak - 保护 Spring Boot 应用程序 - Keycloak - securing a Spring Boot application 将本地 React 前端连接到本地 Spring Boot 中间件应用程序时出现 CORS 错误 - CORS error when connecting local React frontend to local Spring Boot middleware application CORS 发送 post 请求时出错,但在 Spring Boot 和 Reactjs 中未获取请求 - CORS error when post request is send, but not get request in Spring Boot and Reactjs 如何修复使用POST api将对象从reactjs应用程序传递到spring boot应用程序时出现的错误? - How to fix errors in passing an object from reactjs application to spring boot application using POST api? 从 ReactJS 应用程序访问 Azure IOT 集线器时出现错误 -“被 CORS 策略阻止” - Error -“Blocked by CORS policy” when accesing Azure IOT Hub from ReactJS application Reactjs 应用程序无法使用 https 请求访问我的 Spring Boot 后端 - Reactjs application can not reach my Spring Boot backend using https requests 如何在 ReactJs 应用程序中读取 Spring 启动应用程序属性? - How to read Spring Boot application.properties in ReactJs app? 从ReactJS进入REST服务时出现CORS问题 - Getting CORS issue , when hitting REST service from ReactJS 将 ReactJS 应用程序部署到 Heroku 时出现应用程序错误 - Application error when deploying ReactJS app to Heroku
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM