Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CORS Filter not working as intended

I am trying to send a request from my Webstorm application to my backend application, which both are at different ports, I am working with angularJS in the front end and java in backend. I have read up a bit about CORS Filters and I learned that in order to do Cross Origin Requests I need to implement these. However, after doing this my error, being

Failed to load resource: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:63343' is therefore not allowed access. http://localhost:8080/register?password=&username= XMLHttpRequest cannot load http://localhost:8080/register?password=&username=. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:63343' is therefore not allowed access. 

did not change at all which led me to believe I have done something wrong, here is the code from which I am sending the request:

var charmanderServices = angular.module('charmanderServices', ['ngResource']);  var hostAdress = "http://localhost:8080";  charmanderServices.factory("register", ["$resource",     function($resource){         console.log('in service');         return $resource(hostAdress + "/register", {}, {             'registerUser' : { method: 'POST', isArray: false,             params: {                 username: '@username',                 password: '@password'             }             },             headers : {'Content-Type' : 'application/x-www-form-urlencoded'}         });      } ]); 

My corsFilter is written like this:

@Component public class SimpleCORSFilter implements Filter {      public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {          //This is not even printing         System.out.println("Cheers lads, I'm in the filter");         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, X-Auth-Token, Content-Type");         chain.doFilter(req, res);     }      public void init(FilterConfig filterConfig) {}      public void destroy() {} } 

This is my web.xml:

<web-app version="3.0"          xmlns="http://java.sun.com/xml/ns/javaee"          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"          xsi:schemaLocation="http://java.sun.com/xml/ns/javaee          http://java.sun.com/xml/ns/javaee/web-app_3_1.xsd">      <display-name>Project</display-name>       <!-- Load Spring Contexts -->     <listener>         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>     </listener>      <!-- CORS Filter -->      <filter>         <filter-name>cors</filter-name>         <filter-class>com.robin.filters.SimpleCORSFilter</filter-class>     </filter>      <filter-mapping>         <filter-name>cors</filter-name>         <url-pattern>/*</url-pattern>     </filter-mapping>      <servlet>         <servlet-name>dispatcherServlet</servlet-name>         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>         <init-param>             <param-name>contextConfigLocation</param-name>             <param-value>classpath:/spring/applicationContext.xml</param-value>         </init-param>         <load-on-startup>1</load-on-startup>     </servlet>      <servlet-mapping>         <servlet-name>dispatcherServlet</servlet-name>         <url-pattern>/</url-pattern>     </servlet-mapping>  </web-app> 

This is the controller where I am catching the request:

@Controller public class UserController {      @Autowired     private UserService userService;      @RequestMapping(value = "/register" , method= RequestMethod.POST, produces = "application/json")     @ResponseBody     public boolean register(@RequestParam(value = "username") String username, @RequestParam(value = "password") String password){         System.out.println("Im in register hurray");         return userService.register(username, password);     } } 

Update: I have tried implementing the filter as a OncePerRequestFilter, still doesn't work. Is anyone able to help me further here?

Update#2: Also tried this one, http://software.dzhuvinov.com/cors-filter-installation.html, no luck

Update#3: This was my output in the console, I can see that the response did not add any headers:

Request URL:http://localhost:8080/register?password=g&username=g Request Method:OPTIONS Status Code:200 OK Request Headersview source Accept:*/* Accept-Encoding:gzip,deflate,sdch Accept-Language:en-US,en;q=0.8,nl;q=0.6 Access-Control-Request-Headers:accept, content-type Access-Control-Request-Method:POST Connection:keep-alive Host:localhost:8080 Origin:http://localhost:63343 Referer:http://localhost:63343/Project/index.html?uName=g&uPassword=g&uPasswordConfirm=g User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.152 Safari/537.36 Query String Parametersview sourceview URL encoded password:g username:g Response Headersview source Allow:GET, HEAD, POST, PUT, DELETE, OPTIONS Content-Length:0 Date:Fri, 04 Apr 2014 09:50:35 GMT Server:Apache-Coyote/1.1 

Update#4: Annotated the filter with @WebFilter instead of @Component, didn't help

Update#5: Here is my applicationContext.xml file:

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"        xmlns:context="http://www.springframework.org/schema/context"        xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">     <context:component-scan base-package="com.robin"/>     <mvc:annotation-driven/>     <!-- Hibernate Session Factory -->     <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">         <property name="dataSource" ref="dataSource"/>         <property name="packagesToScan">             <array>                 <value>com.robin.model</value>             </array>         </property>         <property name="hibernateProperties">             <props>                 <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>                 <prop key="hibernate.show_sql">true</prop>                 <prop key="hibernate.hbm2ddl.auto">create</prop>             </props>         </property>         <property name="annotatedClasses">             <list>                 <value>com.robin.model.User</value>             </list>         </property>     </bean>      <tx:annotation-driven transaction-manager="transactionManager"/>      <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">         <property name="sessionFactory" ref="sessionFactory"/>     </bean>      <!--Driver for mysqldb -->     <import resource="mysql-context.xml"/> </beans> 

I also added the code from my controller and register.html file here:

charmanderControllers.controller('registerController', ['$scope', 'register',      function($scope, register){     $scope.username = '';     $scope.password = '';      $scope.register = function () {         register.registerUser({'username': $scope.username, 'password': $scope.password}).$promise.then(function(data){             switch(data.response){                 case true:                     //succes                     $scope.registered = true;                     $scope.userExists = false;                     break;                 case false:                     //user exists                     $scope.registered = false;                     $scope.userExists = true;                     break;             }             console.log(data.response);         })     };          $scope.checkValidRegister = function (invalid) {             console.log(invalid);             console.log($scope.passwordConfirm);             console.log($scope.password);             console.log($scope.username);             if (invalid || $scope.password != $scope.passwordConfirm) {                 console.log("I shouldnt be here");                 $scope.validation = true;                 if ($scope.password != $scope.passwordConfirm) {                     $scope.passwordError = true;                 }             } else {                 register();             }         }; }]); 

register.html

 <h1>Register now!</h1>     <form method="post" class="register" novalidate>         <p>             <label for="email">E-mail:</label>             <input type="text" name="login" id="email" placeholder="E-mail address..." required ng-model="username">         </p>          <p>             <label for="password">Password:</label>             <input type="password" name="password" id="password" placeholder="Password..."                    required ng-model="password">         </p>          <p>             <label for="confirm_password">Confirm password: </label>             <input type="password" name="confirm_password" id="confirm_password" placeholder="Confirm password..."                    required ng-model="passwordConfirm">             <span ng-show="passwordError">Passwords do not match!</span>         </p>          <p class="register_submit">             <button type="submit" class="register-button" ng-click="checkValidRegister()">Register</button>         </p>      </form> 
like image 423
Robin-Hoodie Avatar asked Apr 03 '14 18:04

Robin-Hoodie


1 Answers

Your code and configuration looks good in general and I was able to run it on local environment. Please remove @Component annotation from your SimpleCORSFilter, because you use it as a plain Servlet Filter and it doesn't need to be a part of Spring context.

UPD. Tomcat 7 has its own CORS filter implementation. You can check documentation and source code for more details. I have modified the headers to reflect its default configuration, should work as expected now.

Since you are already using Spring and if you are using Spring 4.2 or higher, you dont need CorsFilter, but can simply annotate your controller method with CrossOrigin. Here is the excellent article on this, worth a read.

Please also check nice resource which describes CORS configuration for different platforms.

Here is my working example using plain Filter implementation:

Controller:

@Controller public class UserController {      private static Logger log = Logger.getAnonymousLogger();      @RequestMapping(             value = "/register",             method = RequestMethod.POST,             consumes = "application/x-www-form-urlencoded")     @ResponseBody     public String register(@RequestParam(value = "user") String username,                            @RequestParam(value = "password") String password) {         log.info(username + " " + password);         return "true";     }  } 

Filter:

public class CorsFilter implements Filter {      private static final Logger log = Logger.getAnonymousLogger();      @Override     public void init(FilterConfig filterConfig) throws ServletException {      }      @Override     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {         log.info("Adding Access Control Response Headers");         HttpServletResponse response = (HttpServletResponse) servletResponse;         response.setHeader("Access-Control-Allow-Origin", "*");         response.setHeader("Access-Control-Allow-Credentials", "true");         response.setHeader("Access-Control-Allow-Methods", "POST, GET, HEAD, OPTIONS");         response.setHeader("Access-Control-Allow-Headers", "Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");         filterChain.doFilter(servletRequest, servletResponse);     }      @Override     public void destroy() {      } } 

Filter mapping in web.xml:

    <filter>         <filter-name>cors</filter-name>         <filter-class>com.udalmik.filter.CorsFilter</filter-class>     </filter>      <filter-mapping>         <filter-name>cors</filter-name>         <url-pattern>/register</url-pattern>     </filter-mapping> 

JS to perform request from separate web app (JQuery):

$(document).ready(function() {     $('#buttonId').click(function() {         $.ajax({             type: "POST",             url: "http://localhost:8080/register",             success : function(data){                 console.log(data);             },             data : {                 user : '[email protected]',                 password : 'password'             }         });     } } 
like image 185
udalmik Avatar answered Oct 11 '22 14:10

udalmik