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.
- 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;
}
- Swapping my
WebConfig.java
with the one provided by @Knox, everything works on HOST, still (CORS request did not succeed) from the outside
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
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:
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:
The above allows frontends hosted at
http://192.168.1.x:3333
andhttp://localhost:3333
to use/api/...
endpoints.Here is your error
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 aslocalhost: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 onlocalhost: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 be192.168.1.x:8080
as you say that this is reachable from some user outside of the machine where the server runs.I think that is important to be sure what methods is allowed to make the CORS request.
@Bean