如何在本地主机上启用 Spring 和 Angular 2 之间的 CORS

Posted

技术标签:

【中文标题】如何在本地主机上启用 Spring 和 Angular 2 之间的 CORS【英文标题】:How to enable CORS between Spring and Angular 2 on localhost 【发布时间】:2021-02-14 15:43:48 【问题描述】:

我在 Spring 4.3.4.RELEASE 和 Spring 安全版本 4.2.2.RELEASE 上有一个 Java 后端的旧项目。没有办法更新库,我必须使用现有的。该项目不是 REST API,没有 RestController 也没有 WebMvcConfigurer。所以,我按照here 的建议配置了 cors:

@EnableWebSecurity
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter 

    protected SecurityConfiguration() 
        super();
    

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception 
        httpSecurity
                // we don't need CSRF because our token is invulnerable
                .csrf().disable()

                // don't create session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()

                .authorizeRequests()

                // allow anonymous resource requests
                .antMatchers(HttpMethod.GET,
                        "/",
                        "/*.html",
                        "/favicon.ico",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js")
                .permitAll()
                .antMatchers(HttpMethod.POST, "/rpc/*").permitAll()
                .anyRequest().permitAll();

        httpSecurity.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class);

        // disable page caching
        httpSecurity.headers().cacheControl();
    

Cors 过滤器:

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CorsFilter extends OncePerRequestFilter 

    private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(CorsFilter.class);

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException 
        logger.error("CORS filter: " + request.getMethod());
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With, xsrf-token");
        response.addHeader("Access-Control-Expose-Headers", "xsrf-token");
        if ("OPTIONS".equals(request.getMethod())) 
            response.setStatus(HttpServletResponse.SC_OK);
         else 
            filterChain.doFilter(request, response);
        
    

在 web.xml 中所有 context-param 之后和 listeners 之前(这是应用程序中的唯一过滤器):

   <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>web.servlet.response.CorsFilter
        </filter-class>
    </filter>

    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

我在 localhost:8080 上运行后端,在 localhost:8081 上运行前端 但是,当我尝试发送带有 application/json 的 POST 请求时,浏览器控制台中出现错误

Access to XMLHttpRequest at 'http://localhost:8080/rpc' from origin 'http://localhost:8081' 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.

另外,我希望在后端看到日志“CORS filter: POST”,我把它放在那里检查它是否会进入过滤器,但没有这样的日志。

请,关于为什么它不起作用的任何建议?我需要在请求中坚持 application/json ,并且有时在应用程序中还有一个 Authorization 标头,因此无法完全避免 cors。这可能是 Angular 2 的问题吗?

【问题讨论】:

【参考方案1】:

我不熟悉spring或java,但我想我理解这个问题。

我假设您的日志过滤器不起作用,因为 spring 框架在您的过滤器执行之前短路了该请求。

这里的问题可能在配置中的某个地方,但我认为解决给定问题的方法通常是不正确的。

CORS 是出于某些目的而创建的,并且通过允许来自所有其他来源的浏览器请求,您实际上会产生一些潜在的安全问题。

理想情况下,如果服务器和客户端都是您的应用程序,则从客户端您应该只向托管客户端应用程序的同一源发出请求。因此,在您的本地情况下,它应该向localhost:8081 发出请求,您可以通过在发出请求时省略客户端的主机基地址来做到这一点。

要让它工作,对于开发环境,最好的选择是为 Angular 设置代理(代理从 localhost:8081localhost:8080 的请求),详细的设置信息是 here。

对于生产环境,您需要在同一源上托管后端服务器/客户端应用程序。您可以通过在后端服务器/客户端 Web 服务器前面放置一些代理服务器,或者直接在后端服务器上托管客户端应用程序(角度静态输出文件)来做到这一点。

另一种选择是做您尝试做的事情,但不允许来自所有来源的请求(不是.permitAll()),而是列出特定允许的来源(如localhost:8081)。 但它不是那么干净,而且您需要为开发环境和生产环境指定不同的来源。

【讨论】:

感谢您的建议。我尝试配置它,但遇到了问题。如果您熟悉 Angular,您也可以看看这个问题吗? ***.com/q/64681061/6834824【参考方案2】:

这个错误是拒绝OPTION web方法的结果,所以你应该在configure方法中添加如下语句:

.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()

像这样:

httpSecurity
            // we don't need CSRF because our token is invulnerable
            .csrf().disable()

            // don't create session
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()

            .authorizeRequests()

            // allow anonymous resource requests
            .antMatchers(HttpMethod.GET,
                    "/",
                    "/*.html",
                    "/favicon.ico",
                    "/**/*.html",
                    "/**/*.css",
                    "/**/*.js")
            .permitAll()
            .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
            .antMatchers(HttpMethod.POST, "/rpc/*").permitAll()
            .anyRequest().permitAll();

【讨论】:

不幸的是,这没有帮助。 @Katrikken 抱歉,我不小心放了 .antMatchers(HttpMethod.POST, "/api/auth/login").permitAll() 而不是 .antMatchers(HttpMethod.OPTIONS, "/**")。 permitAll(),我不确定你放的是后者。 我编辑了答案以显示正确的答案,也许它会有所帮助 是的,我注意到了,但仍然没有。我尝试按照下面的建议配置代理,但由于某种原因它也不起作用。

以上是关于如何在本地主机上启用 Spring 和 Angular 2 之间的 CORS的主要内容,如果未能解决你的问题,请参考以下文章

如何访问在 AWS EC2 Ubuntu 的本地主机上运行的 Parse 服务器?

Spring Boot 应用程序在本地主机上运行,​​但在外部 Tomcat 上返回 404

如何在本地主机上设置带有和不带有 www 前缀的子域/域

您如何在本地主机上使用 https / SSL?

如何在本地主机上运行 html 文件?

如何在我的本地主机上创建子域? [关闭]