抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

摘要:本文学习了Spring MVC中的跨域请求处理。

环境

Windows 10 企业版 LTSC 21H2
Java 1.8
Tomcat 8.5.50
Maven 3.6.3
Spring 5.3.23

1 概述

跨域(Cross Origin Resource Sharing,CORS)是指从一个域名的网页去请求另一个域名的资源。由于浏览器的同源策略,默认情况下不允许跨域请求,这是为了保护用户信息安全。

同源策略要求请求的协议、域名和端口都必须相同,否则就是跨域请求。

出现跨域时,浏览器控制台会提示:

log
1
2
Access to XMLHttpRequest at '目标URL' from origin '网页URL' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.

2 预检请求

浏览器在发送跨域请求时,如果请求是复杂请求,浏览器会先发送OPTIONS预检请求,询问服务器是否允许该跨域请求。

请求分为简单请求和复杂请求,简单请求会直接发送请求,复杂请求会先发送OPTIONS预检请求。

2.1 简单请求

简单请求的要求,必须全部满足:

  • 请求方法是简单类型:
    • GET
    • POST
    • HEAD
  • 请求头中只包含简单的字段
    • Accept:告知客户端浏览器支持的响应类型。
    • Accept-Language:告知服务器浏览器支持的语言。
    • Content-Language:告知服务器传输内容使用的语言。
    • Content-Type:告知浏览器传输内容使用的内容类型,只能是application/x-www-form-urlencodedmultipart/form-datatext/plain其中之一。

简单请求对服务器没有安全影响,浏览器会直接发送请求。

2.2 复杂请求

复杂请求的要求,满足一个即可:

  • 请求方法不是简单类型。
  • 请求头中包含自定义请求头,或者Content-Type是JSON格式或者XML格式。

复杂请求对服务器有安全影响,浏览器会先发送OPTIONS预检请求,询问服务器是否允许该跨域请求。

服务器在收到OPTIONS预检请求后,会在响应头中添加允许跨域的相关字段:

  • Access-Control-Allow-Origin:指定允许跨域请求的域名,*表示允许所有域名。
  • Access-Control-Allow-Methods:指定允许跨域请求的方法,多个方法用逗号分隔。
  • Access-Control-Allow-Headers:指定允许跨域请求的请求头,多个头用逗号分隔。
  • Access-Control-Expose-Headers:指定允许客户端访问的响应头,多个头用逗号分隔。
  • Access-Control-Allow-Credentials:指定是否允许跨域请求携带认证信息,设置为true时必须指定允许跨域请求的域名。
  • Access-Control-Max-Age:指定预检请求的缓存时间,单位秒。

3 解决方案

常见的跨域解决方案包括:

  • JSONP:通过在请求中添加回调函数参数,将响应包裹在函数调用中,实现跨域,只支持GET请求。
  • CORS:通过在服务器端设置响应头,允许指定的域名跨域请求。
  • 代理:通过在服务器端设置代理,将跨域请求转发到目标服务器,再将响应返回给客户端。

4 配置方式

4.1 局部配置

使用@CrossOrigin注解可以类级别和方法级别配置跨域,传入允许跨域请求的域名。

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
// 在类级别配置跨域
@Controller
@RequestMapping("/api")
@CrossOrigin(origins = "http://localhost:8080")
public class DemoController {
// 在方法级别配置跨域,会覆盖类级别的配置
@RequestMapping("/demo")
@CrossOrigin(origins = "http://localhost:8080")
@ResponseBody
public String demo() {
return "demo";
}
}

4.2 全局配置

4.2.1 半注解配置

spring-mvc.xml配置文件中使用mvc:cors标签配置全局跨域策略:

spring-mvc.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?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"
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">
<!-- 启用对象扫描 -->
<context:component-scan base-package="com.example.controller"/>

<!-- 启用注解驱动 -->
<mvc:annotation-driven/>

<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
<property name="contentType" value="text/html;charset=UTF-8"/>
</bean>

<!-- 配置全局跨域策略 -->
<mvc:cors>
<mvc:mapping path="/api/**"
allowed-origins="http://localhost:8080"
allowed-methods="GET, POST, PUT, DELETE, OPTIONS"
allowed-headers="Origin, Content-Type, Accept, Authorization"
exposed-headers="Content-Length"
allow-credentials="true"
max-age="3600"/>
</mvc:cors>
</beans>

4.2.2 全注解配置

在配置类中配置全局跨域策略:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Configuration
@EnableWebMvc
@ComponentScan("com.example.controller")
public class WebConfig implements WebMvcConfigurer {
// 配置视图解析器
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setContentType("text/html;charset=UTF-8");
return resolver;
}
// 配置跨域
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://localhost:8080")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("Origin", "Content-Type", "Accept", "Authorization")
.exposedHeaders("Content-Length")
.allowCredentials(true)
.maxAge(3600);
}
}

评论