how to enable CORS with Vert.x 2.x - angularjs

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.

Related

Enable CORS in springboot mail API

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 {}

HTTP POST turns into OPTIONS AngularJS

I'm trying to submit a login form to a rest Api, translating my Jquery/Javascript code into AngularJS. I try to use $http service for send the request, but when I submit the form, the POST request turns into OPTIONS and no request params are passed to. This is my code:
controller.formData = {
username : $scope.formData.username,
password : $scope.formData.password,
};
$http({
method : 'POST',
url : 'http://localhost:8080/multe-web/signin',
data : controller.formData,
headers : { 'Content-Type': 'application/json' }
})
.success(function(data) {
console.log(data);
});
This is a screenshoot of browser's console
Why no parameters are passed to the HTTP POST request?
Can someone help me?
If you are trying to do a Cross Origin Request, this could be a 'preflight request':
See this post: How to disable OPTIONS request?
This request is called "preflight request". This appends when you try to access a resource that is from another domain. For more information you can search for Cross-origin resource sharing (CORS). During the preflight request, you should see headers starting with Access-Control-Request-* These request headers are asking the server for permissions to make the actual request. Your preflight response needs to acknowledge these headers in order for the actual request to work.
The OPTIONS that you see is a pre-flighted request that is initiated by the browser. This happens due to CORS that stands for "CROSS ORIGIN RESOURCE SHARING`.
Pre-flighted requests
In your case, your backend server needs to acknowledge the OPTIONS request and send a 200 back. Only then the browser fires the real POST request
Here are some links to get you started
Same Origin policy
AngularJS and CORS
Similar question on SO
You must define a CORS filter on your backend.
I don't know which language you are using but an example in spring framework (java) would be the following.
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleCorsFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization, cache-control, content-type, Origin, key");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void destroy() {
}
}
Basically you are stating that response.setHeader("Access-Control-Allow-Origin", "*"); can come from any domain. ( in a production environment you would limit this offcourse to your known domains ).
When implementing this filter your options request will pass through without problems.
The options call is something the browser does automatically and you really don't want to disable this as some other answers are suggesting.
Kind regards

Angular JS and Spring MVC #RestController - POST status 0

I am currently try to develop RESTful application using Angular on font-end and Spring MVC on back-end(#RestController). I already have implemented few GET and POST method to fetch or save data, however now I am facing a problem with one POST method:
angular.module("app").factory("BorrowerService", function($http, $log){
var borrowBook = function(borrow){
$log.debug("Borrowing book: " );
$log.debug(borrow);
$http({
method: 'POST',
url: 'Zadanie3/borrow',
data: borrow
}).then(function successCallback(response) {
$log.debug("success");
}, function errorCallback(response) {
$log.error("failed to borrow the book");
$log.error(response);
});
};
return{
borrowBook: borrowBook
};
});
POST result is always errorCallback with output:
failed to borrow the book
Object {data: null, status: 0, config: Object, statusText: ""}
Interesting part is that on most browsers (except Firefox) the book actually do borrow. I have already searched about status:0 response (also on stackoverflow) and unfortunatelly have not found the correct solution to my problem. I would appreciate any help. Here my RestController method:
#RestController
public class BookRestPostController {
#RequestMapping(value = "/borrow", method=RequestMethod.POST)
public BorrowDTO borrow(#RequestBody BorrowDTO borrow ) {
borrowerService.borrowBook(borrow);
return borrow;
}
}
Edit: I forgot to mention that I use #RestController annotation on my controller class. It automatically includes #ResponseBody.
For security reasons some of the browsers don't allow to make ajax request that are not in the same origin. If you want read more on CORS I'm recommending this article. You have several choices to resolve your problem.
The first one is if you want to keep your current projects structure, both projects
to exists on different servers. You should enable CORS (Cross origin resource sharing) support on server side and I see that you are using newer Spring Framework so you can just add one
annotation that will enable CORS support. The annotation is #CrossOrigin see this article.
#RestController
#CrossOrigin(maxAge = 3600)
public class BookRestPostController {
#RequestMapping(value = "/borrow", method=RequestMethod.POST)
public BorrowDTO borrow(#RequestBody BorrowDTO borrow ) {
borrowerService.borrowBook(borrow);
return borrow;
}
}
If you are using older version of Spring Framework you can do in the manual way - add Filter which will add needed response headers. See this article
#Component
public class SimpleCORSFilter implements Filter {
#Override
public void init(FilterConfig arg0) throws ServletException {}
#Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
// TODO Auto-generated method stub
HttpServletResponse response=(HttpServletResponse) resp;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
chain.doFilter(req, resp);
}
#Override
public void destroy() {}
}
This is probably the cleanest way to achieve your goal.
Other two alternatives are to create server side proxy or to deploy both client and server side code in one server but my suggestion is not use proxy or same server !.
With simple words the browser will preflight request, it will send before every request, one extra request of type OPTIONS with specific headers (Access-Control-Request-Method and Access-Control-Request-Headers). These request headers will ask the server for permissions to make the actual request. Your preflight response needs to acknowledge these headers in order for the actual request to work.
Damn annoying bugOrFeature...
add #ResponseBody annotation
public #ResponseBody BorrowDTO borrow

How to debug "No 'Access-Control-Allow-Origin' header is present on the requested resource"

I am getting this error shown on browser console:
XMLHttpRequest cannot load http://localhost:8080/api/login. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:9009' is therefore not allowed access.
The environment I am using are:
Backend- Spring Boot
Front End- Angularjs
Web Server- Grunt
On server, I am already defining headers in request and response as:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setHeader("Access-Control-Allow-Origin", "*");
httpResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE,PUT");
httpResponse.setHeader("Access-Control-Max-Age", "3600");
httpResponse.setHeader("Access-Control-Allow-Headers", "x-requested-with,Authorization, Content-Type");
if (httpRequest.getMethod().equals("OPTIONS")) {
httpResponse.setStatus(HttpServletResponse.SC_ACCEPTED);
return;
}
}
I already found this question on this link No 'Access-Control-Allow-Origin' header is present on the requested resource but couldn't find the appropriate solution.
Here is the browser network image:
Your server should add the proper CORS headers for preflight/actual CORS requests. I wouldn't recommend implementing your own Filter, since this is a rather complex specification.
As of 4.2, Spring Framework supports CORS with a global configuration or with a Filter - see this blog post for more information.
If you're using Spring Boot, version 1.3.0 (to be released soon) will integrate this support.
You need to enable CORS(Cross Origin Resource Sharing) on your web server. Please refer to this resource.
You need to set your response header to:
Access-Control-Allow-Origin: *
This link has all the info needed to set CORS
Enabling CORS for all domain is not a good practice, so you should restrict it when everything is up and running.
Another workaround is to have a separate API for curl request
Call a URL in your own API and access the data using your server side with cURL or something. I have a current working project with same situation (Laravel + AngularJs).
My sample code for the remote auth check with cURL request.
Code is in Laravel-PHP format, I hope you can convert to the language you are working on.
Requester function:
public function curlRequester($url,$fields)
{
// Open connection
$ch = curl_init();
// Set the url, number of POST vars, POST data
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true );
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
// Execute post
$result = curl_exec($ch);
// Close connection
curl_close($ch);
$result_json_decoded = json_decode($result, true);
return $result_json_decoded;
}
Controller function
public function login()
{
// set POST params
$fields = array(
'username' => Input::get('username'),
'password' => Input::get('password')
);
$url = 'http://www.this-is-api-url.com/login';
$result = $this->curl->curlRequester($url,$fields);
return response()->json($result);
}
Angular request function
$scope.authCheck = function(){
$http({
url:'http://www.our-project-url/login',
method:"post",
data:{"username": "rameez", "password":"rameezrami"}
})
.success(function(response) {
if(response.status==1){
$location.path("/homepage");
}else if(response.status==0){
$scope.login_error_message_box = true;
$scope.login_error_message_text =response.message;
}
});
}
Your Filter is right, You should include your filter as a component and put it where it can be scanned by spring
#Component
public class CORSFilter implements Filter{
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
chain.doFilter(req, res);
}
#Override
public void destroy() {
}
}
You need to also make an update in your angular code to allow CORS via AJAX.
myApp.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}
]);
If the above doesn't work, you may need to also add the following:
myApp.all('/*', function (request, response, next) {
response.header("Access-Control-Allow-Origin", "*");
response.header("Access-Control-Allow-Headers", "X-Requested-With");
response.header("Access-Control-Allow-Methods", "GET, POST", "PUT", "DELETE");
next();
});
As noted by #Marged you want to be cautious about the "*" and I would highly recommend replacing that with your domain(s).

AngularJS: X-Auth-Token not sent (error 401)

I'm here because I tried everything and I don't understand what I'm doing wrong.
As a backend, I use Spring MVC, Rest and Spring Security. I'm using a custom authentication's system based on tokens (entirely stateless). I've also a CORS's filter, because my front app is not running on the same server.
So here's what I did in angularJS, in order to get authenticate when I want to access a protected resource:
I've created an interceptor that change each request to add the token found from a cookie
I add a custom header on the request
Here's what my interceptor do:
request: function(config) {
var token = $cookieStore.get("token");
if (token && token.key) {
config.headers["X-Auth-Token"]=token.key;
}
console.log(config.headers);
return config || $q.when(config);
}
But when I try to access a protected resource, even if the token is still valid, it never puts the X-Auth-Token headers on the request! It tried to send an option's request and failed with a 401 error!
But if I do the same with a url parameter (like token=....), the interceptor works as expected...(I've implemented both system : by parameter and by header)
What I don't understand is why the header is not put as expected?? and why it works perfectly with something like POSTMAN for instance?
Please help, I'm losing my hairs with this...
It's probably cleaner to do this instead of your accepted answer.
if (request.getMethod().equals("OPTIONS")) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(request, response);
}
I fixed the problem by changing my filter like this:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
LOG.info("goes on the filter");
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Origin, Authorization, X-Auth-Token");
response.addHeader("Access-Control-Expose-Headers", "X-Auth-Token");
if (request.getMethod().equals("OPTIONS")) {
try {
response.getWriter().print("OK");
response.getWriter().flush();
} catch (IOException e) {
LOG.error(e.getMessage());
}
} else {
chain.doFilter(request, response);
}
}
Not sure if this will solve your issue, but $cookieStore can only get/set strings, so the following:
var token = $cookieStore.get("token");
if (token && token.key) {
Will never evaluate to true.

Resources