文章目录
跨域资源共享的策略与实践
1. 引言
1.1 CORS的重要性
随着Web应用的发展,越来越多的应用需要从不同的源加载资源和服务。例如,一个网站可能需要从另一个域名获取数据、图像或其他资源。然而,出于安全原因,浏览器实施了一种名为“同源策略”(Same-origin policy)的安全机制,限制了从不同源加载资源的能力。这导致了跨域资源共享(CORS)作为一种解决方案出现,它允许服务器明确指定哪些源可以访问其资源。
CORS通过添加额外的HTTP头部信息来实现跨域访问控制,从而确保只有经过授权的站点才能获取到资源。这对于保护用户隐私和防止恶意行为至关重要。
1.2 跨域问题背景介绍
跨域问题源于浏览器的安全策略,即同源策略。这一策略要求脚本只能访问与其来源相同的资源。来源通常由协议、域名和端口号共同定义。例如,https://example.com:8080
和 https://example.com:80
虽然域名相同,但因为端口不同而被视为不同的源。
这种策略阻止了来自其他源的脚本读取响应正文,除非服务器明确指示允许这样做。为了克服这一限制,开发人员需要使用CORS来设置适当的HTTP头部,允许跨源请求。
2. HTTP基础知识
2.1 HTTP请求方法
HTTP协议定义了一系列请求方法,用于指示客户端希望执行的操作。最常用的方法包括:
- GET:请求特定的资源。
- POST:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。
- PUT:替换目标资源的当前内容。
- DELETE:删除指定资源。
- HEAD:类似于GET,但只请求响应头部。
- OPTIONS:请求查询服务器的性能,或者查询与资源相关的选项。
2.2 HTTP请求标头
HTTP请求标头提供了关于请求的附加信息。这些标头可以包含认证信息、请求优先级、媒体类型偏好等。一些常见的请求标头包括:
- Accept:客户端可接受的内容类型列表。
- Authorization:用于认证的凭据。
- Content-Type:发送实体主体的媒体类型。
- Origin:发起请求的域名。
2.3 HTTP响应标头
HTTP响应标头同样提供了关于响应的元数据。这些标头包含了关于服务器状态、缓存策略和其他相关信息。重要的响应标头包括:
- Content-Type:响应的数据类型。
- Cache-Control:缓存机制的指令。
- Date:生成响应的时间。
- Server:服务器软件的信息。
3. CORS基础概念
3.1 CORS定义
跨域资源共享(CORS)是一种机制,它使用额外的HTTP头部让服务器能够指明某个实际请求或者预检请求是否被允许。CORS也定义了一个过程来确定浏览器是否允许请求成功完成。
3.2 CORS的工作原理
CORS的基本工作流程涉及浏览器检查HTTP响应标头中的Access-Control-Allow-Origin
字段,以确定是否允许请求继续。如果该字段包含请求发起者的域名或者包含特殊值*
(星号),则浏览器认为该请求是被允许的。
3.3 同源策略解释
同源策略(SOP)是浏览器的一种安全机制,它限制了一个网页中的脚本只能与同一源的页面进行交互。源是指一个URL的协议、主机名和端口号的组合。例如,https://www.example.com:80
和 https://www.example.com:443
是两个不同的源,因为端口号不同。
当一个脚本尝试访问来自不同源的资源时,浏览器会阻止这种尝试,除非服务器明确通过CORS机制允许这种跨域请求。
4. CORS请求类型
4.1 简单请求
简单请求是指那些不需要预检请求的HTTP请求。这类请求满足以下条件:
- 使用了以下方法之一:GET、HEAD 或 POST。
- 如果使用的是POST方法,则请求体中的数据类型必须是
application/x-www-form-urlencoded
、multipart/form-data
或text/plain
。 - 请求标头中只包含简单的头部,如
Accept
、Content-Type
、Last-Event-ID
等。
简单请求直接由浏览器发送,不需要额外的步骤。
4.2 预检请求
预检请求是在某些特定情况下发送的,用于询问服务器是否允许跨域请求。这类请求发生在:
- 使用了除GET、HEAD、POST之外的HTTP方法。
- POST请求的Content-Type不是
application/x-www-form-urlencoded
、multipart/form-data
或text/plain
。 - 请求标头中包含了非简单头部。
预检请求使用OPTIONS
方法,并且浏览器在发送实际请求之前先发送预检请求。预检请求的目的是确定服务器是否允许实际的跨域请求。如果预检请求被允许,那么浏览器会发送实际的请求。
4.3 弱CORS (Weak CORS)
弱CORS是相对于强CORS而言的概念,指的是浏览器对于某些资源(如图像、音频或视频)的自动放宽同源策略的行为。在弱CORS的情况下,浏览器允许跨域请求,即使服务器没有显式地允许CORS。
弱CORS通常应用于不敏感的数据,比如图像、音频或视频文件。这意味着开发者不需要在服务器端显式地设置CORS标头,就可以在HTML元素中加载这些资源。但是,弱CORS不允许脚本访问这些资源的响应数据。
5. HTTP响应标头详解
5.1 Access-Control-Allow-Origin
这个标头用来指定哪些源可以访问服务器的资源。它可以是一个具体的域名,也可以是通配符*
来表示所有源都可以访问。
- 示例:
Access-Control-Allow-Origin: https://example.com
- 示例:
Access-Control-Allow-Origin: *
5.2 Access-Control-Allow-Methods
这个标头用来列出允许客户端使用的HTTP方法。这个标头通常用于预检请求的响应中。
- 示例:
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
5.3 Access-Control-Allow-Headers
这个标头用来列出客户端可以使用的HTTP标头字段。它通常用于预检请求的响应中。
- 示例:
Access-Control-Allow-Headers: X-Requested-With, Content-Type
5.3 Access-Control-Max-Age
这个标头用来指定预检请求的结果可以被缓存的秒数。这样可以避免频繁地发送预检请求。
- 示例:
Access-Control-Max-Age: 86400
5.5 Access-Control-Allow-Credentials
这个标头用来指示是否允许请求携带身份验证信息,如cookies或HTTP认证信息。
- 示例:
Access-Control-Allow-Credentials: true
5.6 Access-Control-Expose-Headers
这个标头用来列出客户端可以访问的响应标头。通常情况下,客户端只能访问某些默认的响应标头。
- 示例:
Access-Control-Expose-Headers: X-Custom-Header, X-Another-Custom-Header
6. HTTP请求标头详解
6.1 Origin
这个标头用来指示请求的原始域名。服务器使用这个标头来决定是否允许跨域请求。
- 示例:
Origin: https://example.com
6.2 Access-Control-Request-Method
这个标头用来指定预检请求中实际请求所要使用的HTTP方法。
- 示例:
Access-Control-Request-Method: POST
6.3 Access-Control-Request-Headers
这个标头用来列出预检请求中实际请求将要使用的HTTP标头。
- 示例:
Access-Control-Request-Headers: X-Requested-With, Content-Type
7. Java中的CORS配置
7.1 Spring框架中的CORS配置
1. 使用Spring Web MVC
在Spring Web MVC中配置CORS可以通过多种方式实现,包括使用拦截器、过滤器或者全局配置类。
使用全局配置类:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("https://example.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
在这个例子中,我们通过addCorsMappings
方法为所有的路径(/**
)配置了CORS。允许的来源设置为https://example.com
,并且允许GET
, POST
, PUT
, DELETE
方法。此外,还允许所有标头(*
),允许携带凭据,并设置了最大年龄为3600秒。
使用过滤器:
创建一个自定义的过滤器类来添加CORS标头。
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setHeader("Access-Control-Allow-Origin", "https://example.com");
httpResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
httpResponse.setHeader("Access-Control-Allow-Headers", "*");
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Max-Age", "3600");
chain.doFilter(request, response);
}
// ...
}
然后在Spring配置类中注册此过滤器。
2. 使用Spring Boot
Spring Boot提供了一种更简单的方式来配置CORS,只需要在application.properties
或application.yml
文件中添加配置即可。
使用配置文件:
spring:
mvc:
cors:
registration:
paths: "/**"
allowed-origins: "https://example.com"
allowed-methods: GET, POST, PUT, DELETE
allowed-headers: "*"
allow-credentials: true
max-age: 3600
这与上面的代码配置非常相似,只是通过配置文件来实现。
7.2 Servlet API中的CORS配置
使用Servlet API配置CORS可以通过创建一个自定义的过滤器来实现。
创建自定义过滤器:
public class CustomCorsFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setHeader("Access-Control-Allow-Origin", "https://example.com");
httpResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
httpResponse.setHeader("Access-Control-Allow-Headers", "*");
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Max-Age", "3600");
chain.doFilter(request, response);
}
// ...
}
然后在web.xml
文件中注册此过滤器。
7.3 使用第三方库如CorsFilter
还有许多第三方库可以帮助简化CORS的配置,例如Springfox的CorsFilter
。
添加依赖:
在pom.xml
文件中添加依赖:
<dependency>
<groupId>com.github.yadnyeshmule</groupId>
<artifactId>cors-filter</artifactId>
<version>2.6</version>
</dependency>
配置CorsFilter:
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("https://example.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
8. 常用的CORS策略
8.1 允许所有来源
允许所有来源意味着任何源都可以访问服务器资源。虽然这简化了配置,但可能会增加安全风险。
配置示例:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(false)
.maxAge(3600);
}
}
8.2 允许指定来源
允许指定来源意味着只有列出的源才能访问服务器资源。这是一种更为安全的配置方式。
配置示例:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("https://example.com", "https://another.example.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
8.3 安全策略建议
- 限制允许的源:尽可能限制允许的源数量。
- 限制允许的方法:只允许必要的HTTP方法。
- 限制允许的标头:只允许必要的HTTP标头。
- 控制凭据:如果可能,禁止携带凭据。
9. 安全性与隐私考虑
9.1 跨站请求伪造(CSRF)
CORS机制本身并不能完全防止CSRF攻击,但可以采取措施减少风险。例如,通过设置Access-Control-Allow-Credentials
为false
,可以禁止携带凭据,从而降低CSRF的风险。
9.2 数据泄露风险
CORS配置不当可能导致敏感数据泄露。例如,如果允许所有来源访问敏感API端点,则可能会导致数据泄露。确保仅允许可信来源访问敏感资源。
9.3 身份验证和凭据
CORS配置应该与身份验证机制相协调。例如,如果API端点需要身份验证,那么应该允许携带凭据,并且只允许可信来源。
10. 最佳实践
10.1 设定合理的Access-Control-Allow-Origin
- 限制允许的源:尽量只允许可信的源访问您的资源。
- 避免使用通配符:虽然使用
*
方便,但它会降低安全性。最好明确列出允许的源。 - 动态配置:根据需要动态配置允许的源,例如从数据库或配置文件中读取。
10.2 控制暴露的响应标头
- 仅暴露必要的标头:避免暴露不必要的响应标头,特别是那些包含敏感信息的标头。
- 使用Access-Control-Expose-Headers:明确列出客户端可以访问的响应标头。
10.3 设置Access-Control-Allow-Credentials
- 仅在需要时允许凭据:如果不需要凭据,应将
Access-Control-Allow-Credentials
设置为false
。 - 确保HTTPS:如果允许携带凭据,应确保使用HTTPS来加密通信。
11. 常见问题解答
11.1 什么是预检请求?
预检请求是浏览器发送的一个特殊的OPTIONS
请求,用于询问服务器是否允许即将进行的实际请求。这种请求在特定情况下发生,例如当请求方法不是GET
、HEAD
或POST
时,或者当请求包含自定义标头时。预检请求的目的在于避免不必要的跨域请求,同时确保请求符合服务器的CORS策略。
11.2 如何调试CORS错误?
- 查看浏览器控制台:大多数现代浏览器都提供了网络调试工具,可以在控制台中查看HTTP请求和响应的详细信息。
- 检查请求标头:确保
Origin
标头正确设置,并且预检请求的Access-Control-Request-Method
和Access-Control-Request-Headers
标头正确。 - 检查响应标头:确认服务器的响应包含正确的CORS标头,例如
Access-Control-Allow-Origin
和Access-Control-Allow-Methods
。
11.3 CORS与安全令牌
- 令牌传递:如果您的应用使用了安全令牌(如JWT),确保令牌可以通过CORS请求正确传递。
- 令牌验证:服务器端需要验证接收到的令牌,确保它们未被篡改并且有效。
- 令牌刷新:如果支持令牌刷新机制,确保CORS策略允许刷新请求。
12. 总结
12.1 CORS的关键要点回顾
- 同源策略:浏览器的安全策略,限制了从不同源加载资源的能力。
- CORS机制:通过HTTP标头允许服务器明确指定哪些源可以访问其资源。
- 请求类型:简单请求和预检请求,后者用于复杂的跨域请求。
- HTTP标头:包括请求标头和响应标头,用于实现CORS机制。
- Java配置:使用Spring框架或Servlet API来配置CORS。
- 安全性考虑:确保CORS配置不会引入安全漏洞。
12.2 对未来的展望
随着Web技术的发展,CORS机制也在不断进化。未来可能会有更多的标准和最佳实践出现,以增强跨域访问的安全性和灵活性。开发人员需要持续关注新的安全威胁和发展趋势,以确保他们的应用始终保持最新的安全实践。
13. 参考资料
官方文档
博客和技术文章
社区论坛