Enable CORS in springboot mail API - reactjs

I have easy mail sending springboot API and reactjs app for fetch.
Hosted in nginx server.
React app running well localhost, email working everything okei but if I run on a server it gives an error.
I have tried to add different Nginx server settings and add Springboot
#CrossOrigin(origins = "http://xx.xxx.xxx.xxx/") but have not found any help so far.
Where do I have to allow CORS if I deploy to server?
Nginx server default:
server {
listen 80 default_server;
root /var/www/name/build;
server_name xx.xxx.xxx.xxx;
index index.html index.htm;
location / {
}
}
Springboot:
#PostMapping
public void sendFeedback(#RequestBody Feedback feedback,
BindingResult bindingResult){
if(bindingResult.hasErrors()){
throw new ValidationException("Feedback is not valid");
}
// Create a mail sender
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
Properties props = mailSender.getJavaMailProperties();
mailSender.setHost(this.emailCfg.getHost());
mailSender.setPort(this.emailCfg.getPort());
mailSender.setUsername(this.emailCfg.getUsername());
mailSender.setPassword(this.emailCfg.getPassword());
// Create an email instance
SimpleMailMessage mailMessage = new SimpleMailMessage();
mailMessage.setFrom(feedback.getEmail());
mailMessage.setTo("test#gmail.com");
mailMessage.setSubject("Testing mail");
mailMessage.setText(feedback.getFeedback());
mailSender.send(mailMessage);
}
Reactjs code I have:
fetch(url,{
method: 'POST',
headers:{
'Content-Type': 'application/json'
},
body: JSON.stringify(state)
}).then(response =>{
console.log(data)
}).catch(error =>{
console.log(error)
})
Output:
Access to fetch at 'http://xx.xxx.xxx.xxx:8080/feedback-0.0.1-SNAPSHOT' from
origin 'http://xx.xxx.xxx.xxx' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check:
Redirect is not allowed for a preflight request.

Add a CORS config as below :
CORSConfig.java
#Configuration
public class CORSConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD");
}
}
OR
Annonate your controller
#CrossOrigin
#PostMapping
public void sendFeedback(#RequestBody Feedback feedback,
BindingResult bindingResult){
if(bindingResult.hasErrors()){
throw new ValidationException("Feedback is not valid");
}
// Create a mail sender
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
Properties props = mailSender.getJavaMailProperties();
mailSender.setHost(this.emailCfg.getHost());
mailSender.setPort(this.emailCfg.getPort());
mailSender.setUsername(this.emailCfg.getUsername());
mailSender.setPassword(this.emailCfg.getPassword());
// Create an email instance
SimpleMailMessage mailMessage = new SimpleMailMessage();
mailMessage.setFrom(feedback.getEmail());
mailMessage.setTo("test#gmail.com");
mailMessage.setSubject("Testing mail");
mailMessage.setText(feedback.getFeedback());
mailSender.send(mailMessage);
}

This is caused by your server not responding with the proper headers. If you look at your network tab you will see and OPTIONS request that fires before your POST request. Your server needs to respond to that OPTIONS request to let the browser know its ok for the requesting client to access the resource. You can read more about it here at MDN but at the very least your server should respond to the preflight with headers like:
Access-Control-Allow-Origin: http://foo.example <== your domain here
Access-Control-Allow-Methods: POST, OPTIONS
Access-Control-Allow-Headers: Content-Type

#CrossOrigin(origins="http://localhost:9000")
#GetMapping("/hello")
public Greeting greeting() {
return "world";
}
or
#CrossOrigin(origins="http://localhost:9000", maxAge=3600)
#RestController
public class RestController {}

Related

Getting Axios from React to Work with Spring Boot + Spring Boot Security

I have created a react app with a spring boot backend but I'm having trouble pulling the data through with axios.
I have checked numerous SO posts as well as documentation with spring to no avail. I was initially blocked by CORS but I was able to resolve that with Spring Security. However, spring security requires authentication, I've been using the default user "user" with the randomly generated password (since I can't get a newly defined user/password defined with AuthenticationManagerBuilder to work just with queries against the server directly in a browser but that's an issue for another day). Below is my configuration file for the server.
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic();
http.cors().and();
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:3000"));
configuration.setAllowedMethods(Arrays.asList("GET", "PUT", "POST", "DELETE", "OPTIONS", "HEAD"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
My server runs on localhost port 9898, the query I'm initially trying to pull data from on the front end is a get by id for contact info which goes against http://localhost:9898/api/Contact/1
when I successfully call the server from a browser directly the header details are as depicted:
call from browser to server general and response headers
call from browser to server request headers
notice that authorization header is actually there in the request.
For the RestController I've got cross origin set to the client running on port 3000. I do have a header being adding in the getContactMethod as suggested in a tutorial but I don't think this actually changed anything since I have this header being set in the configuration file anyway.
#CrossOrigin(origins = "http:localhost:3000")
#RestController
#RequestMapping("/api/Contact")
public class ContactController {
#Autowired
private ContactRepository ContactRepository;
#GetMapping("/")
public List<Contact> getContacts(){
return this.ContactRepository.findAll();
}
#GetMapping("/{id}")
public Contact GetContact(#PathVariable Long id, HttpServletResponse response){
response.setHeader("Access-Control-Allow-Origin", "**");
return ContactRepository.findById(id).orElse(null);
}
For the client I have a file creating an axios instance, I'm not sure if this part is right since I never reference the specific instance again but maybe axios can figure this out on it's own if there is only one instance.
import axios from "axios";
const api = axios.create({
baseURL: "http://localhost:9898",
});
// api.defaults.headers.common = {
// 'X-Requested-With': 'XMLHttpRequest'
// };
export default axios;
Now for the actual page on the front end I am attempted to load the requested data into the state variable from the useEffects event, which will need to be modified a little bit further but I can't go forward with that until the request works.
I've got numerous headers loaded in based on a combination of what I've come across online but the one I want to focus on is the authentication since that won't actually show up in the request when I look with dev tools on the network. I've got the password which is randomly set by spring security each time the back end is run hard coded and then this hard coded user:password value is encoded and added to the headers. Below is the relevant code:
const tok = 'user:9de3a921-a4af-4d51-b8d7-cf37b208916e';
const hash = btoa(tok);
const Basic = 'Basic ' + hash;
const headers = {
"Cache-Control": "no-cache",
"Accept-Language": "en",
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "http://localhost:3000",
"Access-Control-Allow-Methods": "DELETE, POST, GET, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With",
//"Authorization": "Basic dXNlcjowM2VhN2JhYS1mMTQ0LTQ5YWMtOGFhMy02NDE4YWJiNzdhMTk=",
'Authorization': `Basic ${hash}`,
};
useEffect(() =>{
console.log(Basic);
axios.get("http://localhost:9898/api/Contact/1", headers)
.then((res) => {
console.log("data " + res.data);
console.log("response header " + res.headers['Authorization']);
setInfo(res.data);
}).catch(err => console.log("error found " + err));
console.log(info);
}, []||[]);
When this is run I get a 401 unauthorized but for some reason the authorization header doesn't show up in the request headers.
General and response headers for request from client to server
Request headers for request from client to server
I feel like I'm fairly close with this but most of the tutorials on the spring site are simpler and the best practices for spring security have changed over the years so there is a lot of conflicting information and incomplete examples on the web. I figure I either have an issue in the security configuration or I guess I've set the headers up incorrectly but I don't have enough experience with spring and react I've just been troubleshooting in circles for a couple days.
Sources tried already (had to put some spaces in the links since I just made this account to post a question):
https://stackoverflow com/questions/36968963/how-to-configure-cors-in-a-spring-boot-spring-security-application/37610988#37610988
I should mention with this one below I added in .antMatchers(HttpMethod.Options, "/**").permitAll() and the headers were different but the request still didn't work and eventually the server would just crash shortly after starting with it
https://stackoverflow com/questions/41075850/how-to-configure-cors-and-basic-authorization-in-spring-boot/60933851#60933851
https://stackoverflow com/questions/58901586/how-to-fix-spring-security-authorization-header-not-being-passed
https://spring io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter
https://spring io/guides/gs/rest-service-cors/
https://spring io/guides/gs/rest-service/
https://docs.spring io/spring-security/reference/reactive/integrations/cors.html
https://www.baeldung com/spring-security-cors-preflight
There is an issue with how the headers are are being passed to axios. The axios documentation defines axios.get like this axios.get(url[, config])
There are two parameters here. The first is the url, and it is required. The second is an optional config object.
The config object has a headers field.
You should pass in the headers like this:
const headers = {
'Cache-Control': 'no-cache',
'Accept-Language': 'en',
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': 'http://localhost:3000',
'Access-Control-Allow-Methods': 'DELETE, POST, GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Requested-With',
//"Authorization": "Basic dXNlcjowM2VhN2JhYS1mMTQ0LTQ5YWMtOGFhMy02NDE4YWJiNzdhMTk=",
Authorization: `Basic ${hash}`
};
const config = {
headers
};
axios.get('http://localhost:9898/api/Contact/1', config);
I was looking back at the similar post I referenced earlier How to configure CORS in a Spring Boot + Spring Security application?
and I tried out the 4th most updated answer by user Soroosh Khodami Mar 11, 2021 and used their SecurityConfig.
#Override
protected void configure(HttpSecurity http) throws Exception {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedHeaders(List.of("Authorization", "Cache-Control", "Content-Type"));
corsConfiguration.setAllowedOrigins(List.of("http://localhost:3000"));
corsConfiguration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PUT","OPTIONS","PATCH", "DELETE"));
corsConfiguration.setAllowCredentials(true);
corsConfiguration.setExposedHeaders(List.of("Authorization"));
// You can customize the following part based on your project, it's only a sample
http.authorizeRequests().antMatchers("/**").permitAll().anyRequest()
.authenticated().and().csrf().disable().cors().configurationSource(request -> corsConfiguration);
}
My previous config was missing:
setAllowedHeaders(Listof("Authorization",..))
setAllowCredentials(true)
setExposedHeaders(Authorization)
this then lead me to a secondary issed which was caused by setting the header in my restcontroller get by id method.
response.setHeader("Access-Control-Allow-Origin", "**");
I got an error saying this was not allowed so then I changed it to "/**" and it still complained so I just commented it out and it worked. So if a tutorial suggests this (for me it was a youtube video about getting cors to work) I believe it is out of date/not best practice when you should set Access-Control-Allow-Origin in your Web Security Configuration.

React JS send post request to Springboot Rest Server and get error

I am hosting my React app in localhost:3000, and hosting my SpringBoot RESTServer at localhost:8080, tried to send a post request to that server and got following error:
Access to XMLHttpRequest at 'http://localhost:8080/employees' 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.
but i already put Access-Control-Allow-Origin in my code:
axios.post('http://localhost:8080/employees', params, {
headers: {
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json',
'mode': 'no-cors'
}
}
)
API tested with postman and it worked.
Please help me.
That is caused by the CORS issue. You can google it and acquire lots of explanation which will tell you why the request is blocked when you call an api through the browser.
Basically, the CORS issue of Access-Control-Allow-Origin could be solved by setting you api endpoint. For sprint-boot api, you can try this two way.
Add #CrossOrigin onto the specific path controller
#CrossOrigin(origins = "http://localhost:3000")
#GetMapping("/employees")
public Employees getEmployees() {
// ...
}
Set the global CORS configuration
#Configuration
public class MyConfiguration {
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
// Allow all origin to call your api
registry.addMapping("/**");
}
};
}
}
You can reference this article for more clearly CORS settings in spring-boot.

Access-Control-Allow-Origin Problem in ReactJS

I have an issue when I try to access the backend.
Questions:
Do have any configure CORS in ReactJs?(in axios create or ..)
Are there any best way to do the CORS configuration in Spring Boot and ReactJs?
configurations are following below what I done,
Access to XMLHttpRequest at 'http://localhost:8282/opsprime/api/products?title_contains=a' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
So, I configured the backend(spring boot) as,
#RestController
#CrossOrigin(origins = "*", allowedHeaders = "*")
public class TestController {
....
}
and this is the reactJs configuration,
export default axios.create({
baseURL: 'http://localhost:8282/opsprime/api',
timeout: 1000,
headers: {
'Authorization' : `Bearer 4b21949a-4829-43cc-asex-1d0512478676`
}
});
Add this code to your spring boot application configuration, you can change the value of CORS_ORIGIN_ALLOW to identify on which methods CROSS can be implemented.
#Configuration
public class CorsSecurity implements WebMvcConfigurer {
private final String[] CORS_ORIGIN_ALLOW = {"/**"};
#Override
public void addCorsMappings(CorsRegistry registry) {
for (String cors : CORS_ORIGIN_ALLOW)
registry.addMapping(cors).allowedOrigins("*").allowedHeaders("*").allowedMethods("*");
}
}

React fetch response missing Authorization header

I have some issues with CORS using React fetch and Spring Boot.
The React application runs on localhost:3000.
The Spring Boot backend runs on localhost:3001.
My problem is when I try to logging in using using fetch with the http://localhost:3001/login url the response in javascript does not contain the Authorization token.
The authentication on backend side works.
When I open the Chrome Inspector I can see the Authorization in the Network tab at the login request only it is missing in the javascript response.
The React fetch request look like the following: In the code the const userToken = response.headers.get('Authorization'); returns "null" string instead of the token.
return fetch("http://localhost:3001/login",{
method: 'post',
headers: {
"Accept": "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({
email,
password
})
})
.then(
response => {
if(response.ok) {
const userToken = response.headers.get('Authorization');
return true;
}
// Error handling
}
);
The Spring Boot Security config is like the following:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf()
.disable().authorizeRequests()
.antMatchers(HttpMethod.POST, REGISTRATION_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(// Auth Filter)
.addFilter(// Another auth Filter)
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
}
Another thing. When I used proxy: "http://127.0.0.1:3001" in the package.json the login worked and the React code above could read Authorization header. But I don't want to use proxy.

how to enable CORS with Vert.x 2.x

I am trying to make cross-domain requests with Angularjs 1.4.5.
But can't get success. I have configured $httpprovider
.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
$httpProvider.defaults.headers.common['Accept']= "application/json, text/plain, */*";
$httpProvider.defaults.headers.put["Content-Type"] = "application/x-www-form-urlencoded;charset=utf-8";
$httpProvider.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded;charset=utf-8";
$httpProvider.interceptors.push('authenticationFailedInterceptor');
}])
But still con't get success. How to enable CORS support with Vert.x 2.x http server.
CORS is supported in Vert.x 3.x but Right now I can't upgrade Vert.x.
have you tried with something like this on your responses?
vertx.createHttpServer()
.requestHandler(function (req) {
req.response()
.putHeader("content-type", "text/plain")
.putHeader("Access-Control-Allow-Origin", "*")
.putHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
.putHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
.end("Hello from Vert.x!");
}).listen(8080);
for sure, you have to modify this to your needs... but the changes has to be done in the server and at least, you need these three headers.
Complete example to enable cors:
We need to create two route matcher.
One helps to enable cors and other handle the requests.
Below is to enable cors. It Accept all request and add all the required headers needs to enable cors. After that we need to hand over request to the actual route matcher to handle request. We have that by the name secureRoutes.
RouteMatcher routeMatcher = new RouteMatcher();
routeMatcher.options(".*",new Handler<HttpServerRequest>() {
#Override
public void handle(final HttpServerRequest request) {
request.response().putHeader("Access-Control-Allow-Origin", "*");
request.response().putHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
request.response().putHeader("Access-Control-Allow-Headers", "accept, authorization, content-type, email");
request.response().end();
}
})
.all(".*",new Handler<HttpServerRequest>() {
#Override
public void handle(final HttpServerRequest request) {
request.response().putHeader("Access-Control-Allow-Origin", "*");
secureRoutes.getRouteMatcher().handle(request);
}
});
Another route matcher:
public class SecureRoutes {
private static final RouteMatcher routeMatcher = new RouteMatcher();
#Inject
protected Container container;
#Inject
private SigninController signinController;
#Inject
private SignupController signupController;
#Inject
private OauthController oauthController;
#Inject
ClientNetworkSignalController clientNetworkSignalController;
public void initRoutes() {
// APP routes. they use User token for authentication
routeMatcher.get("/", new Handler<HttpServerRequest>() {
#Override
public void handle(final HttpServerRequest request) {
request.response().putHeader("Cache-Control",
"public, max-age=86400");
request.response().sendFile("web/public/index.html");
}
});
routeMatcher.post("/signin", signinController.signin());
routeMatcher.post("/signup", signupController.signup());
routeMatcher.post("/oauth2/token", oauthController.token());
routeMatcher.post("/oauth2/invalidate_token", oauthController.invalidateToken());
}
public RouteMatcher getRouteMatcher() {
return routeMatcher;
}
}
Now finally add requestHandler to server:
server.requestHandler(routeMatcher).listen(port,
host, new Handler<AsyncResult<HttpServer>>() {
public void handle(AsyncResult<HttpServer> asyncResult) {
if (asyncResult.succeeded()) {
logger.info(s + ": Started on " + host + ":"
+ port);
} else {
logger.info(s + ": Unable to start server.\n "
+ asyncResult.cause());
}
}
});
You may have a question What is the use of http options type request handler. The answer is for that is very interesting. Javascript is a secured language that do not allow Cross origin Http request. So, to allow cross origin request javascript send a options type request for each http request request and check weather CORS is supported or not. In such Javascript hits server two times one to check cors is supported or not and one to fatch data.

Resources