如何在本地主机上的 Spring 和 Angular 2 之间启用 CORS
How to enable CORS between Spring and Angular 2 on localhost
我有一个旧项目,Java 后端在 Spring 版本 4.3.4.RELEASE 和 Spring 安全版本 4.2.2.RELEASE 上。没有办法更新库,我必须使用现有的库。该项目不是 REST API,没有 RestController 也没有 WebMvcConfigurer。所以,我按照建议配置了 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 和 listerenrs 之前(这是应用程序中唯一的过滤器):
<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 过滤器:POST”,我把它放在那里是为了检查它是否会进入过滤器,但没有这样的日志。
拜托,关于为什么它不起作用有什么建议吗?我需要在请求中坚持application/json,而且有时在应用程序中还有一个授权header,所以无法完全避免cors。这可以是 Angular 2 问题吗?
我不熟悉 spring 或 java,但我想我理解了这个问题。
我假设您的日志记录过滤器不起作用,因为 spring 框架在您的过滤器执行之前短路了该请求。
这里的问题可能出在配置的某个地方,但我认为一般情况下解决给定问题的方法是不正确的。
CORS 是出于某些目的而创建的,通过允许来自所有其他来源的浏览器的请求,您实际上会造成一些潜在的安全问题。
理想情况下,如果服务器和客户端都是您的应用程序,那么从客户端您应该只向托管客户端应用程序的同一来源发出请求。因此,在您的本地情况下,它应该向 localhost:8081
发出请求,您可以通过在发出请求时省略客户端的主机基地址来做到这一点。
要使其正常工作,对于开发环境,最好的选择是为 angular 设置代理(代理从 localhost:8081
到 localhost:8080
的请求),详细的设置信息是 here.
对于生产环境,您需要在同一来源上托管 backend-server/client 个应用。您可以通过在您的 backend-server/client 网络服务器前面设置一些代理服务器,或者直接在您的 backend-server.
上托管客户端应用程序(angular 静态输出文件)来做到这一点
另一种选择是执行您尝试执行的操作,但不允许来自所有来源(不是 .permitAll()
)的请求,而是 - 列出特定的允许来源(例如 localhost:8081
)。
但这并不是那么干净,您还需要为开发环境和生产环境指定不同的来源。
这个错误是拒绝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();
我有一个旧项目,Java 后端在 Spring 版本 4.3.4.RELEASE 和 Spring 安全版本 4.2.2.RELEASE 上。没有办法更新库,我必须使用现有的库。该项目不是 REST API,没有 RestController 也没有 WebMvcConfigurer。所以,我按照建议配置了 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 和 listerenrs 之前(这是应用程序中唯一的过滤器):
<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 过滤器:POST”,我把它放在那里是为了检查它是否会进入过滤器,但没有这样的日志。
拜托,关于为什么它不起作用有什么建议吗?我需要在请求中坚持application/json,而且有时在应用程序中还有一个授权header,所以无法完全避免cors。这可以是 Angular 2 问题吗?
我不熟悉 spring 或 java,但我想我理解了这个问题。
我假设您的日志记录过滤器不起作用,因为 spring 框架在您的过滤器执行之前短路了该请求。
这里的问题可能出在配置的某个地方,但我认为一般情况下解决给定问题的方法是不正确的。
CORS 是出于某些目的而创建的,通过允许来自所有其他来源的浏览器的请求,您实际上会造成一些潜在的安全问题。
理想情况下,如果服务器和客户端都是您的应用程序,那么从客户端您应该只向托管客户端应用程序的同一来源发出请求。因此,在您的本地情况下,它应该向 localhost:8081
发出请求,您可以通过在发出请求时省略客户端的主机基地址来做到这一点。
要使其正常工作,对于开发环境,最好的选择是为 angular 设置代理(代理从 localhost:8081
到 localhost:8080
的请求),详细的设置信息是 here.
对于生产环境,您需要在同一来源上托管 backend-server/client 个应用。您可以通过在您的 backend-server/client 网络服务器前面设置一些代理服务器,或者直接在您的 backend-server.
上托管客户端应用程序(angular 静态输出文件)来做到这一点另一种选择是执行您尝试执行的操作,但不允许来自所有来源(不是 .permitAll()
)的请求,而是 - 列出特定的允许来源(例如 localhost:8081
)。
但这并不是那么干净,您还需要为开发环境和生产环境指定不同的来源。
这个错误是拒绝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();