skip to Main Content

I’m trying to deploy a React + Spring Boot App.

3 docker containers with compose (client+server+database) and everything works on localhost. I can access the frontend, login/register, and use the web service as expected.

client: localhost:3333
server: localhost:8080

Once i try deploy it on another machine (desktop w/ ubuntu server), im able to acess the frontend / backend and send requests via postman (ex: POST a new user to 192.168.1.x:8080/api/account/register.)

192.168.1.x:3333
192.168.1.x:8080

Problem:

If i try to acess 192.168.1.x:3333 or 192.168.1.x:8080 in another machine (same network) or my-ip:333
(another network) it works, but pressing Login/Register wont work. (this only works on the host machine)

Devtools Firefox:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8080/api/account/login. (Reason: CORS request did not succeed). Status code: (null).

Devtools Chrome:

Failed to load resource: net::ERR_CONNECTION_REFUSED localhost:8080/api/account/login:1          
TypeError: Failed to fetch

EDIT (23/05/2022)

I still couldn´t solve the issue

WebSecurity.java

@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
    private UserDetailsService userDetailsService;
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    private DataSource dataSource;

    public WebSecurity(UserDetailsService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
        this.userDetailsService = userDetailsService;
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {

        // Comment to disable authentication
        http.cors().and().csrf().disable().authorizeRequests().antMatchers(HttpMethod.POST, ACESSABLE).permitAll()
                .anyRequest().authenticated().and().addFilter(new JWTAuthenticationFilter(authenticationManager()))
                .addFilter(new JWTAuthorizationFilter(authenticationManager()))
                // this disables session creation on Spring Security
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        // auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
        auth.jdbcAuthentication().dataSource(dataSource)
                .usersByUsernameQuery("select username, password, 1 from users where username=?")
                .authoritiesByUsernameQuery(
                        "select u.username, r.name from users_roles ur, users u, role r where u.username = ? and ur.users_id = u.id and ur.role_id = r.id")
                .passwordEncoder(bCryptPasswordEncoder);
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        System.out.println("setting cors configuration");
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.applyPermitDefaultValues();
        source.registerCorsConfiguration("/**", configuration);

        return source;
    }

}

WebConfig.java

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        AntPathMatcher matcher = new AntPathMatcher();
        matcher.setCaseSensitive(false);
        configurer.setPathMatcher(matcher);
    }
}

WHAT I’VE TRIED

tried as @Knox suggested, but still couldn´t make it work.

  1. not specifying allowedOrigins breaks the connection between frontend and backend on HOST machine (CORS ‘Access-Control-Allow-Origin’ missing), but from the outside it just shows the same as allways (CORS request did not succeed).
@Bean
    CorsConfigurationSource corsConfigurationSource() {
        System.out.println("setting cors configuration");
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration configuration = new CorsConfiguration();
        //configuration.applyPermitDefaultValues();
        //configuration.allowedOrigins("*");
        source.registerCorsConfiguration("/**", configuration);

        return source;
    }

enter image description here

  1. Swapping my WebConfig.java with the one provided by @Knox, everything works on HOST, still (CORS request did not succeed) from the outside

enter image description here

EDIT 2 (24/05/2022)

Just noticed my frontend container (React) is not responding to changes… my guess is the URL i’ve changed many times was never getting updated on docker compose restart. So changing URL from localhost:8080 to myip:8080 might fix it if i can get the container to reload.

fetch(URL + '/api/account/login', {

Dockerfile client

FROM node:8

WORKDIR /usr/scr/app
COPY package*.json ./

RUN npm install
COPY . .

CMD ["npm", "start"]

Docker-compose.yml

  client:
    build: ./client
    ports:
      - 3333:3000
    volumes:
      - ./frontend:/usr/src/app
      - /usr/src/app/node_modules
    networks:
      - network_backend



3

Answers


  1. The requests are failing because you are missing a CORS configuration.

    Your browser typically blocks requests that originate from one origin (in your case, http://192.168.1.x:3333) to another (http://192.168.1.x:8080) due to Same-origin policy.

    When you want to allow requests to your backend from a different origin than the one it is hosted on, you’ll need to configure your backend to respond with the appropriate CORS headers that tell the browser that access from a specific, different origin is allowed.

    Your requests might be succeeding when running with localhost as browsers will be more lenient with localhost, rather than something that could be hosted publicly.

    The approaches to configuring CORS in Spring Boot are described here, with one of the methods being annotation-based:

    @CrossOrigin("http://192.168.1.x:3333")
    @RestController
    public class MyCrossOriginController {
    
        // this should now allow requests with an origin of http://192.168.1.x:3333)
        @PostMapping("/api/account/register")
        public Object register(...) {
            // ...
        }
    

    See the Javadocs for the more advanced capabilities of the @CrossOrigin annotation.

    Or, you can register it through the WebMvcConfigurer to have it apply globally or by path:

    @Configuration
    @EnableWebMvc
    public class WebConfig extends WebMvcConfigurerAdapter {
    
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/api/**")
                .allowedOrigins(
                        "http://192.168.1.x:3333",
                        "http://localhost:3333");
        }
    }
    

    The above allows frontends hosted at http://192.168.1.x:3333 and http://localhost:3333 to use /api/... endpoints.

    Login or Signup to reply.
  2. Here is your error

    Failed to load resource: net::ERR_CONNECTION_REFUSED
    localhost:8080/api/account/login:1 TypeError: Failed to
    fetch

    In react you have configured the URL with which the frontend will call the backend server. In that configuration you have wrongly configured the url as localhost:8080.

    The React Frontend normally runs in the clients browser (If you don’t have it configured otherwise) which I suppose is the case also here.

    So when the browser of the client tries to call localhost:8080 it is able to do so when the client sits on the same machine where the backend server runs, as the same machine understands that the backend server runs on localhost:8080.

    But when the client sits to some other machine inside the network, the Frontend (clients browser) tries again to call localhost:8080 but that machine does not have locally the server running. Therefore the error.

    Solution

    Configure the React application code, so that it uses as address to reach the backend not localhost:8080 but some address that is known from all hosts in the network, meaning the ip address of the machine where the server runs. Could be 192.168.1.x:8080 as you say that this is reachable from some user outside of the machine where the server runs.

    Login or Signup to reply.
  3. I think that is important to be sure what methods is allowed to make the CORS request.

    @Bean

    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("http://localhost:3333","http://192.168.1.x:3333"));
        configuration.setAllowedMethods(Arrays.asList("GET","POST","PATCH","DELETE"));
                configuration.setAllowCredentials(Boolean.TRUE);
                //configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search