Issues setting up API on Elastic Beanstalk with HTTPS requests - spring-boot

So, I've been trying for time to get the following working:
Hosting front-end application on S3 Bucket (with HTTPS support)
Hosting Spring Boot API with Elastic Beanstalk (with HTTPS support)
Make requests from front-end application to API (with HTTPS support)
My current setup for EBS is, the API is sitting behind a load balancer with Route 53 directing incoming requests to the load balancer, which then sends an internal HTTP request to EBS.
It was quite painful for me to get to this point (This is all pretty new to me) and after getting this set up, I'm receiving a CORS error when trying to make a request to the API from my front-end. I'm not seeing anything in the logs and my Spring Boot CORS is set up to accept all, so this leads me to believe that the Load Balancer is causing the CORS error.
I'm wondering if I'm on the right track at all? Could the load balancer be causing this issue? Can it be resolved?

Add this code in your main class below main function
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("GET", "POST", "PUT", "DELETE").allowedOrigins("*")
.allowedHeaders("*");
}
};
}

Related

working in the same machine with ReactApp at port 3000, Spring boot war deployed on Tomcat9 at port 8090 but CORS blocked from another machine

In a single machine named mbc the React app at port 3000 running using pm2, the backend Spring boot application demo.war file deployed on Tomcat 9 at port 8099. In the same machine with firefox browser it is working as expected.
But I am facing the problem when from another machine with the firefox browser http://mbc:3000 the UI opens and the fetch call to the backend Spring boot App the sent option call
ends in the CORS blocked. The OPTIONS request returns with CORS blocked. I am not using any Security such as Spring Security. The application is simple proof of concept application using Spring boot and JPA. Kindly guide me how to sort it out. Is this indicates network issue or any other settings to be done at backend server
(1) at each controller #CrossOrigin annotation
(2) at the spring boot application file
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
};
(3) I am not using spring security
(4) In my windows machine at the etc file in the host file added the entry for ipaddress mbc. Here the mbc is the name of the host.

Override CORS policy in java springboot

I am trying to have my frontend server pull an http request from my backend server, but am getting the following error in my browser console:
Access to XMLHttpRequest at 'http://localhost:8080/run' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I know this is a security protocol, but is there an easy way to override this issue and allow for port 8080 (my backend) to return calls from port (3000)?
edit: I am using java springboot as my backend framework and React as my frontend framework
edit 2: I installed and used the Moesif Origin & CORS Changer extension and it works, I just would like to know if there is a more permanent workaround.
A quick approach is to add #CrossOrigin (import is org.springframework.web.bind.annotation.CrossOrigin) to your Rest Controller(s).
You can use CRA proxy setting, too.
This may help:
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("http://localhost:3000");
}
};
}

CORS with Spring Boot - restrict GET requests to certain domains

I am using Spring Boot and would like to restrict HTTP GET requests only from certain domains. For example, I would like to accept requests only from a list of predefined domains (e.g. https://www.frontend.com, https://www.test-frontend.com). How could I implement such a functionality?
I expected to face CORS issues, but theses do not apply for GET requests. Any ideas?
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://www.frontend.com", "https://www.test-frontend.com")
.allowedMethods("GET");
}
Reference: https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/cors.html
First .allowedMethods use for allow methods so if you don't want to allow "GET" then put all others methods but do not put GET method, like below:
.allowedMethods("PUT", "DELETE", "PATCH")
and second you can not CORS by non-webapp clients like curl or Postman, any non web app client.

CORS on Webjars in Spring Boot?

I'm using Spring Boot 1.4.0.M2 and Spring 4.3.0.RC1, trying to enable CORS support in a simple app. I have this added:
#Bean
public WebMvcConfigurer webMvcConfigurerAdapter() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
};
}
I also have webjars pulled in through Gradle, such as:
compile('org.webjars:jquery:2.2.3')
The CORS support works fine with my REST API (via #RestController), but somehow it doesn't seem to work when accessing my webjars. For example (UPDATE: less confusing example), requesting:
http://example.org/webjars/bootstrap-datepicker/1.6.0/package.json
gets a response with the correct content but lacking the expected CORS headers, which of course defeats any actual CORS request. I can't figure out why that would be an exception to the /** mapping. How do I fix it?
JavaScript doesn't need cors configuration . In other words, you can access JavaScript resources from a site which doesn't enable cors.
From wiki site, we can obvious know that a website can freely embed images, stylesheets, scripts, iframes, videos and some plugin content (such as Adobe Flash) from any other domain. So even if you are enabling CORS on your website, but your resource is javascript, so the request does not add CORS header.
Your code snippet copied from documentation is just a sample of showing how to enable CROS in a quick way. This code snippet is not complete. Using this will mess up what WebMvcAutoConfigurationAdapter provided for you.

Spring Websocket in a tomcat cluster

In our current application, we use Spring Websockets over STOMP. We are looking to scale horizontally. Are there any best practices on how we should handle websocket traffic over multiple tomcat instances and how can we maintain session info across multiple nodes.Is there a working sample that one can refer to?
Horizontally scaling WebSockets is actually very different than horizontally scaling stateless/stateful HTTP only based applications.
Horizontally Scaling Stateless HTTP app: just spin up some application instances in different machines and put a load balancer in front of them. There are quite a lot different load balancer solutions such as HAProxy, Nginx, etc. If you are on a cloud environment such as AWS you could also have managed solutions such as Elastic Load Balancer.
Horizontally Scaling Stateful HTTP app: it would be great if we could have all applications being stateless everytime, but unfortunately that's not always possible. So, when dealing with stateful HTTP apps, you must care about the HTTP session, which is a basically a local storage for each different client where the web server can store data that is kept across different HTTP requests (such as when dealing with a Shopping Cart). Well, in this case, when scaling horizontally you should be aware that, as I said, it's a LOCAL storage, so ServerA will not be able to handle an HTTP session that is on ServerB. In other words, if for any reason Client1 that is being served by ServerA starts suddenly to be served by ServerB, his HTTP session will be lost (and his shopping cart will be gone!). The reasons could be a node failure or even a deployment.
In order to address this issue, you can't keep HTTP sessions only locally, that is, you must store them on another external component. That are several components that would be able to handle this, such as any relational database, but that would be actually an overhead. Some NoSQL databases can handle this key-value behavior very well, such as Redis.
Now, with the HTTP session being stored on Redis, if a client starts to be served by another server, it will fetch the client's HTTP session from Redis and load it into its memory, so everything will continue working and the user will not lost his HTTP session anymore.
You can use Spring Session to easily store the HTTP session on Redis.
Horizontally Scaling WebSocket app: When a WebSocket connection is established, the server must keep the connection opened with the client so that they can exchange data in both directions. When a client is listening to a destination such as "/topic/public.messages" we say the client is subscribed to this destination. In Spring, when you use the simpleBroker approach, the subscriptions are kept in memory, so what happens for instance if Client1 is being served by ServerA and wants to send a message using WebSocket to Client2 being served by ServerB? You already know the answer! The message will not be delivered to Client2 because Server1 not even know about the Client2's subscription.
So, in order to address this issue, again you have to externalize the WebSockets subscriptions. As you are using STOMP as a subprotocol, you need an external component that can act as a external STOMP broker. There are quite a lot tools able to do this, but I would suggest RabbitMQ.
Now, you must change your Spring configuration so that it will not keep the subscriptions in-memory. Instead, it will delegate the subscriptions to a external STOMP broker. You can easily achieve this with some basic configurations such as enableStompBrokerRelay.
The important thing to note is that HTTP session is different than WebSocket session. Using Spring Session to store the HTTP session in Redis has absolutely nothing to do with horizontally scaling WebSockets.
I've coded a complete Web Chat Application with Spring Boot (and much more) that uses RabbitMQ as a Full External STOMP Broker and it's public on GitHub so please clone it, run the app in your machine and see the code details.
When it comes to a WebSocket connection loss, there's not much that Spring can do. Actually, the reconnection must be requested by the client side implementing a reconnection callback function, for instance (that's the WebSocket handshake flow, the client must start the handshake, not the server). There are some client side libraries that can handle this transparently for you. That's not SockJS case. In the Chat Application I also implemented this reconnection feature.
Your requirement can be divided into 2 sub tasks:
Maintain session info across multiple nodes: You can try Spring Sessions clustering backed by Redis (see: HttpSession with Redis). This very very simple and already has support for Spring Websockets (see: Spring Session & WebSockets).
Handle websockets traffic over multiple tomcat instances: There are several ways to do that.
The first way: Using a full-featured broker (eg: ActiveMQ) and try new feature Support multiple WebSocket servers (from: 4.2.0 RC1)
The second way: Using a full-feature broker and implement a distributed UserSessionRegistry (eg: Using Redis :D ). The default implementation DefaultUserSessionRegistry using an in-memory storage.
Updated: I've written a simple implementation using Redis, try it if you are interested
To configure a full-featured broker (broker relay), you can try:
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
...
#Autowired
private RedisConnectionFactory redisConnectionFactory;
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableStompBrokerRelay("/topic", "/queue")
.setRelayHost("localhost") // broker host
.setRelayPort(61613) // broker port
;
config.setApplicationDestinationPrefixes("/app");
}
#Bean
public UserSessionRegistry userSessionRegistry() {
return new RedisUserSessionRegistry(redisConnectionFactory);
}
...
}
and
import java.util.Set;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.messaging.simp.user.UserSessionRegistry;
import org.springframework.util.Assert;
/**
* An implementation of {#link UserSessionRegistry} backed by Redis.
* #author thanh
*/
public class RedisUserSessionRegistry implements UserSessionRegistry {
/**
* The prefix for each key of the Redis Set representing a user's sessions. The suffix is the unique user id.
*/
static final String BOUNDED_HASH_KEY_PREFIX = "spring:websockets:users:";
private final RedisOperations<String, String> sessionRedisOperations;
#SuppressWarnings("unchecked")
public RedisUserSessionRegistry(RedisConnectionFactory redisConnectionFactory) {
this(createDefaultTemplate(redisConnectionFactory));
}
public RedisUserSessionRegistry(RedisOperations<String, String> sessionRedisOperations) {
Assert.notNull(sessionRedisOperations, "sessionRedisOperations cannot be null");
this.sessionRedisOperations = sessionRedisOperations;
}
#Override
public Set<String> getSessionIds(String user) {
Set<String> entries = getSessionBoundHashOperations(user).members();
return (entries != null) ? entries : Collections.<String>emptySet();
}
#Override
public void registerSessionId(String user, String sessionId) {
getSessionBoundHashOperations(user).add(sessionId);
}
#Override
public void unregisterSessionId(String user, String sessionId) {
getSessionBoundHashOperations(user).remove(sessionId);
}
/**
* Gets the {#link BoundHashOperations} to operate on a username
*/
private BoundSetOperations<String, String> getSessionBoundHashOperations(String username) {
String key = getKey(username);
return this.sessionRedisOperations.boundSetOps(key);
}
/**
* Gets the Hash key for this user by prefixing it appropriately.
*/
static String getKey(String username) {
return BOUNDED_HASH_KEY_PREFIX + username;
}
#SuppressWarnings("rawtypes")
private static RedisTemplate createDefaultTemplate(RedisConnectionFactory connectionFactory) {
Assert.notNull(connectionFactory, "connectionFactory cannot be null");
StringRedisTemplate template = new StringRedisTemplate(connectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new StringRedisSerializer());
template.afterPropertiesSet();
return template;
}
}
Maintain session info across multiple nodes:
Suppose we have 2 server host, backed up with load balancer.
Websockets are socket connection from browser to specific server host.eg host1
Now if host1 goes down, socket connection from load balancer - host 1 will break.
How spring will reopen same websocket connection from load balancer to host 2 ? browser should not open new websocket connection

Resources