Java CORS 过滤器未按预期工作

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22846309/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 18:20:09  来源:igfitidea点击:

CORS Filter not working as intended

javaangularjstomcatcorsfirebase

提问by Robin-Hoodie

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

我正在尝试将请求从我的 Webstorm 应用程序发送到我的后端应用程序,它们都在不同的端口上,我在前端使用 angularJS,在后端使用 java。我已经阅读了一些关于 CORS 过滤器的内容,我了解到为了执行跨源请求,我需要实现这些。但是,在执行此操作后,我的错误是

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:

我的 corsFilter 是这样写的:

@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.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?

更新:我尝试将过滤器实现为OncePerRequestFilter,但仍然不起作用。有人能在这里进一步帮助我吗?

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

更新#2:也试过这个,http://software.dzhuvinov.com/cors-filter-installation.html,没有运气

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

更新#3:这是我在控制台中的输出,我可以看到响应没有添加任何标题:

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

更新#4:用@WebFilter 而不是@Component 注释过滤器,没有帮助

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

更新#5:这是我的 applicationContext.xml 文件:

<?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:

我还在我的控制器和 register.html 文件中添加了代码:

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

注册.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>

采纳答案by udalmik

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

您的代码和配置总体上看起来不错,我能够在本地环境中运行它。请从您的 中删除@Component注释SimpleCORSFilter,因为您将其用作普通的 Servlet 过滤器,并且它不需要成为 Spring 上下文的一部分。

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.

更新。Tomcat 7 有自己的CORS 过滤器实现。您可以查看文档和源代码以获取更多详细信息。我已经修改了标题以反映其默认配置,现在应该可以按预期工作。

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 articleon this, worth a read.

由于您已经在使用 Spring,如果您使用的是 Spring 4.2 或更高版本,则不需要CorsFilter,但可以简单地使用CrossOrigin. 这是关于这方面的优秀文章,值得一读。

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

另请查看描述不同平台 CORS 配置的好资源。

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:

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):

JS 从单独的 Web 应用程序 (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'
            }
        });
    }
}