马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
跨域-告别CORS烦恼
1-参考网址
- Cors办理跨域哀求题目
- SpringBoot3跨域方案详解
2-思绪整理
1-核心题目
- 1)什么是跨域-为什么要搞一个跨域
- 2)简单哀求/非简单哀求
- 3)理论上怎样办理跨域题目
- 4)Java怎样办理跨域题目
2-个人思索
- 1)为什么有CORS跨域->最简单的表明:前端和后端最好是摆设在同一个服务器上,前后端之间利用内网IP举行访问,克制后端的API接口被其他网站访问(同源计谋)
- 2)但我们为什么没有如许做->多种缘故原由:①前后端分离架构 ②后端多实例容灾 ③等等其他元素 ->前后端分析引发征象:前端必须利用IP大概域名举行访问后端
- 3)又引发运维上的题目->后端的IP大概域名会变,但是前端又不能总是更换后端IP大概域名->一劳永逸的办理方案:后端直接全部放开API的接口调用
- 4)域名不校验岂不是要翻天->办理方案:①前后端鉴权利用API-KEY大概其他鉴权步调 ②公有云的防御服务
- 5)那为什么我当地开发可以访问许多网站的API接口->由于开发职员没有留意这些情况,大概网站本身就是对外提供API访问的网站
3-脑洞打开
- 1)开发侧-如果我做静态个人网站,我怎样举行跨域限定(防止网站被恶意攻击)
- 2)开发侧-如果我做SDK服务型网站,我怎样举行跨域限定(防止网站被恶意攻击)
- 3)爬虫侧-如和克制网站的API接口被爬取,怎样界说本身的robots.txt
4-个人思索-修正版
你的明白总体上是精确的,但有一些地方可以进一步美满和修正:
1-个人思索
- 为什么有CORS跨域
- 最简单的表明
:前端和后端最好是摆设在同一个服务器上,前后端之间利用内网IP举行访问,克制后端的API接口被其他网站访问(同源计谋)。这个说法根本精确,但同源计谋不光是为了克制后端API被其他网站访问,更是为了掩护用户的数据安全,防止恶意网站通过用户欣赏器偷取敏感信息。
- 同源计谋:欣赏器的同源计谋限定了一个文档或脚本从一个源访问另一个源的资源,除非满意同源条件(协议、域名、端口都雷同)。这是欣赏器提供的一种安全机制,用于防止恶意网站读取其他网站的敏感数据。
- 但我们为什么没有如许做
- 多种缘故原由:
- 前后端分离架构:今世Web开发中,前后端分离架构很常见,前端负责展示和交互,后端负责数据处理惩罚和业务逻辑,两者通常摆设在差别的服务器上。
- 后端多实例容灾:为了进步体系的可用性和可靠性,后端大概有多个实例摆设在差别的服务器或数据中心,前端须要访问差别的后端实例。
- 其他元素:比如微服务架构,差别的服务大概摆设在差别的服务器上,前端须要调用多个服务的API。
- 前后端分析引发征象:前端必须利用IP大概域名举行访问后端。这个说法不完全正确,前端可以通过多种方式访问后端,比如通过域名、IP、负载均衡器等。
- 又引发运维上的题目
- 后端的IP大概域名会变:确实,在实际运维中,后端的IP大概域名大概会由于各种缘故原由发生厘革,比如服务器迁移、负载均衡器调解等。
- 但是前端又不能总是更换后端IP大概域名:这个说法精确,频仍更换前端设置不实际,也不方便。
- 一劳永逸的办理方案:后端直接全部放开API的接口调用。这个办理方案不安全,不能简单地全部放开,否则会导致安全风险,比如跨站哀求伪造(CSRF)攻击、恶意爬虫等。
- 域名不校验岂不是要翻天
- 办理方案:
- 前后端鉴权利用API-KEY大概其他鉴步调权:精确,通过API-KEY、OAuth、JWT等方式举行鉴权,可以确保只有授权的哀求才气访问后端API。
- 公有云的防御服务:精确,公有云平台提供了一系列的安全服务,比如防火墙、DDoS防护、WAF(Web应用防火墙)等,可以有效防止恶意攻击。
- 那为什么我当地开发可以访问许多网站的API接口
- 由于开发职员没有留意这些情况:这个说法不完全正确,许多网站的API是对外提供服务的,答应其他网站或开发者调用但,通常会有一些限定,比如须要注册、申请API密钥等。
- 大概网站本身就是对外提供API访问的网站:精确,比如一些开放平台,如微博、微信等,它们的API就是对外提供服务的,供开发者集成到本身的应用中。
2-脑洞打开
- 开发侧-如果我做静态个人网站,我怎样举行跨域限定(防止网站被恶意攻击)
- 跨域限定:可以通过设置CORS(跨域资源共享)计谋来限定哪些网站可以访问你的API。比方,在服务器设置中,只答应特定的域名访问你的API。
- 防止恶意攻击:除了CORS,还可以接纳其他安全步调,比如利用防火墙、限定哀求频率、利用验证码等。
- 开发侧-如果我做SDK服务型网站,我怎样举行跨域限定(防止网站被恶意攻击)
- 跨域限定:同样可以通过CORS计谋来限定访问,还可以要求调用方提供API密钥或其他身份验证信息。
- 防止恶意攻击:可以对哀求举行署名验证,确保哀求的正当性;还可以对哀求举行限流,防止恶意爬虫或攻击导致服务器过载。
- 爬虫侧-如和克制网站的API接口被爬取,怎样界说本身的robots.txt
- 克制被爬取:可以通过技能本领,比如限定哀求频率、验证哀求泉源、利用验证码等,来防止恶意爬虫。
- 界说robots.txt:robots.txt文件是网站根目次下的一个文本文件,用于告诉搜刮引擎和其他爬虫哪些页面可以爬取,哪些不可以爬取。比方:
- User-agent: *
- Disallow: /api/ # 禁止爬虫访问API接口
- Allow: /public/ # 允许爬虫访问公开的资源
复制代码 但要留意,robots.txt只是一种君子协议,恶意爬虫大概会忽略它,以是还须要其他安全步调来掩护API接口。
3-知识整理
1-什么是跨域
一、同源计谋简介
同源计谋(same origin policy)是欣赏器的一个安全功能,差别源的客户端脚本在没有明确授权的情况下,不能读写对方资源。同源计谋是欣赏器安全的基石。
什么是源
源(origin)就是协议、域名和端标语。比方:http://www.baidu.com:80这个URL。
什么是同源
若所在内里的协议、域名和端标语均雷同则属于同源。
是否是同源的判断
比方判断下面的URL是否与 http://www.a.com/test/index.html 同源
- http://www.a.com/dir/page.html 同源
- http://www.child.a.com/test/index.html 差别源,域名不雷同
- https://www.a.com/test/index.html 差别源,协议不雷同
- http://www.a.com:8080/test/index.html 差别源,端标语不雷同
哪些操纵不受同源计谋限定
- 页面中的链接,重定向以及表单提交是不会受到同源计谋限定的;
- 跨域资源的引入是可以的。但是JS不能读写加载的内容。如嵌入到页面中的<script src="..."></script>,<img>,<link>,<iframe>等。
跨域
受前面所讲的欣赏器同源计谋的影响,不是同源的脚本不能操纵其他源下面的对象。想要操纵另一个源下的对象就须要跨域。 在同源计谋的限定下,非同源的网站之间不能发送 AJAX 哀求。
怎样跨域
- 降域
可以通过设置 document.domain='a.com',欣赏器就会以为它们都是同一个源。想要实现以上恣意两个页面之间的通讯,两个页面必须都设置document.domain='a.com' 。
- JSONP跨域
- CORS 跨域
二、CORS 简介
为相识决欣赏器同源题目,W3C 提出了跨源资源共享,即 CORS(Cross-Origin Resource Sharing)。
CORS 做到了如下两点:
- 不粉碎即有规则
- 服务器实现了 CORS 接口,就可以跨源通讯
基于这两点,CORS 将哀求分为两类:简单哀求和非简单哀求。
1、简单哀求
在CORS出现前,发送HTTP哀求时在头信息中不能包罗任何自界说字段,且 HTTP 头信息不凌驾以下几个字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type 只限于 [application/x-www-form-urlencoded 、multipart/form-data、text/plain ] 范例
一个简单的哀求例子:
- GET /test HTTP/1.1
- Accept: */*
- Accept-Encoding: gzip, deflate, sdch, br
- Origin: http://www.examples.com
- Host: www.examples.com
复制代码 对于简单哀求,CORS的计谋是哀求时在哀求头中增长一个Origin字段,服务器收到哀求后,根据该字段判断是否答应该哀求访问。
- 如果答应,则在 HTTP 头信息中添加 Access-Control-Allow-Origin 字段,并返回精确的效果 ;
- 如果不答应,则不在 HTTP 头信息中添加 Access-Control-Allow-Origin 字段 。
除了上面提到的 Access-Control-Allow-Origin ,尚有几个字段用于形貌 CORS 返回效果 :
- Access-Control-Allow-Credentials: 可选,用户是否可以发送、处理惩罚 cookie;
- Access-Control-Expose-Headers:可选,可以让用户拿到的字段。有几个字段无论设置与否都可以拿到的,包罗:Cache-Control、Content-Language、Content-Type
、Expires、Last-Modified、Pragma 。
2、非简单哀求
对于非简单哀求的跨源哀求,欣赏器会在真实哀求发出前,增长一次OPTION哀求,称为预检哀求(preflight request)。预检哀求将真实哀求的信息,包罗哀求方法、自界说头字段、源信息添加到 HTTP
头信息字段中,扣问服务器是否答应如许的操纵。
比方一个DELETE哀求:
- OPTIONS /test HTTP/1.1
- Origin: http://www.examples.com
- Access-Control-Request-Method: DELETE
- Access-Control-Request-Headers: X-Custom-Header
- Host: www.examples.com
复制代码 与 CORS 干系的字段有:
- 哀求利用的 HTTP 方法 Access-Control-Request-Method ;
- 哀求中包罗的自界说头字段 Access-Control-Request-Headers 。
服务器收到哀求时,须要分别对 Origin、Access-Control-Request-Method、Access-Control-Request-Headers 举行验证,验证通事后,会在返回 HTTP 头信息中添加 :
- Access-Control-Allow-Origin: http://www.examples.com
- Access-Control-Allow-Methods: GET, POST, PUT, DELETE
- Access-Control-Allow-Headers: X-Custom-Header
- Access-Control-Allow-Credentials: true
- Access-Control-Max-Age: 1728000
复制代码 他们的寄义分别是:
- Access-Control-Allow-Methods: 真实哀求答应的方法
- Access-Control-Allow-Headers: 服务器答应利用的字段
- Access-Control-Allow-Credentials: 是否答应用户发送、处理惩罚 cookie
- Access-Control-Max-Age: 预检哀求的有效期,单元为秒。有效期内,不会重复发送预检哀求
当预检哀求通事后,欣赏器会发送真实哀求到服务器。这就实现了跨源哀求。
三、Spring Boot 设置 CORS
1、利用@CrossOrigin 注解实现
如果想要对某一接口设置 CORS,可以在方法上添加 @CrossOrigin 注解 :
- @CrossOrigin(origins = {"http://localhost:9000", "null"})
- @RequestMapping(value = "/test", method = RequestMethod.GET)
- public String greetings(){
- return"{"project":"just a test"}";
- }
复制代码 如果想对一系列接口添加 CORS 设置,可以在类上添加注解,对该类声明全部接口都有效:
- @CrossOrigin(origins = {"http://localhost:9000", "null"})
- @RestController
- @SpringBootApplication
- public class SpringBootCorsTestApplication {
- }
复制代码 如果想添加全局设置,则须要添加一个设置类 :
- @Configuration
- public class WebMvcConfig implements WebMvcConfigurer {
- @Override
- public void addCorsMappings(CorsRegistry registry) {
- registry.addMapping("/**")
- .allowedOrigins("*")
- .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
- .maxAge(3600)
- .allowCredentials(true);
- }
- }
复制代码 别的,还可以通过添加 Filter 的方式,设置 CORS 规则,并手动指定对哪些接口有效。
- @Bean
- public FilterRegistrationBean corsFilter(){
- UrlBasedCorsConfigurationSource source=new UrlBasedCorsConfigurationSource();
- CorsConfiguration config=new CorsConfiguration();
- config.setAllowCredentials(true);
- config.addAllowedOrigin("http://localhost:9000");
- config.addAllowedOrigin("null");
- config.addAllowedHeader("*");
- config.addAllowedMethod("*");
- source.registerCorsConfiguration("/**",config); // CORS 配置对所有接口都有效
- FilterRegistrationBean bean=new FilterRegistrationBean(new CorsFilter(source));
- bean.setOrder(0);
- return bean;
- }
复制代码 2、原理分析
无论是通过哪种方式设置 CORS,实在都是在构造 CorsConfiguration。一个 CORS 设置用一个 CorsConfiguration类来表现,它的界说如下:
- public class CorsConfiguration {
- private List<String> allowedOrigins;
- private List<String> allowedMethods;
- private List<String> allowedHeaders;
- private List<String> exposedHeaders;
- private Boolean allowCredentials;
- private Long maxAge;
- }
复制代码 Spring 中对 CORS 规则的校验,都是通过委托给 DefaultCorsProcessor 实现的。
DefaultCorsProcessor 处理惩罚过程如下:
- 判断依据是 Header 中是否包罗 Origin。如果包罗则阐明为 CORS哀求,转到 2;否则,阐明不是 CORS 哀求,不作任那边理惩罚。
- 判断 response 的 Header 是否已经包罗 Access-Control-Allow-Origin,如果包罗,证实已经被处理惩罚过了, 转到 3,否则不再处理惩罚。
- 判断是否同源,如果是则转交给负责该哀求的类处理惩罚。
- 是否设置了 CORS 规则,如果没有设置,且是预检哀求,则拒绝该哀求,如果没有设置,且不是预检哀求,则交给负责该哀求的类处理惩罚。如果设置了,则对该哀求举行校验。
校验就是根据 CorsConfiguration 这个类的设置举行判断:
- 判断 origin 是否正当
- 判断 method 是否正当
- 判断 header是否正当
- 如果全部正当,则在 response header中添加相应的字段,并交给负责该哀求的类处理惩罚,如果不正当,则拒绝该哀求。
2-Java怎样设置CORS
在Spring Boot
3中,办理跨域哀求(CORS,Cross-Origin
Resource Sharing)的题目紧张有以下几种方式:
1. 利用@CrossOrigin注解
你可以直接在Controller类大概详细的哀求处理惩罚方法上利用@CrossOrigin注解来答应跨域哀求。
- @RestController
- @RequestMapping("/user")
- @CrossOrigin(origins = "*")
- public class UserController {
- }
复制代码 在这个例子中,@CrossOrigin
注解被添加到了控制器类上,表现这个控制器下的全部方法都答应来自http://example.com的GET和POST哀求。你也可以将注解添加到特定的方法上,以对该方法应用CORS设置。
2. 全局设置CORS
如果你盼望全局设置CORS,而不是在每个Controller或方法上单独设置,你可以创建一个设置类来实现WebMvcConfigurer接口,并重写addCorsMappings方法。
- import org.springframework.context.annotation.Configuration;
- import org.springframework.web.servlet.config.annotation.CorsRegistry;
- import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
- @Configuration
- public class CorsConfig implements WebMvcConfigurer {
- @Override
- public void addCorsMappings(CorsRegistry registry) {
- registry.addMapping("/**")
- .allowedOrigins("*")
- .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
- .allowedHeaders("*")
- .allowCredentials(true)
- .maxAge(168000);
- }
- }
复制代码 在这个设置中,addMapping("/**")表现对全部的路径都应用CORS设置。allowedOrigins("*")
表现答应全部泉源的访问,这在生产情况中大概不是最佳实践,通常你会指定详细的域名。allowedMethods界说了答应的HTTP方法,allowedHeaders
界说了答应的HTTP头部,allowCredentials(true)表现是否答应携带根据(cookies, HTTP认证及客户端SSL证实等),maxAge则用于设置预检哀求的有效期。
3. 利用Filter实现CORS
你也可以通过实现Filter接口来自界说CORS处理惩罚逻辑。
- import javax.servlet.*;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
- public class SimpleCorsFilter implements Filter {
- @Override
- public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
- throws IOException, ServletException {
- HttpServletResponse response = (HttpServletResponse) res;
- response.setHeader("Access-Control-Allow-Origin", "http://example.com");
- 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");
- chain.doFilter(req, res);
- }
- @Override
- public void init(FilterConfig filterConfig) {
- }
- @Override
- public void destroy() {
- }
- }
复制代码 然后须要在设置类中注册这个Filter。
- import org.springframework.boot.web.servlet.FilterRegistrationBean;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- @Configuration
- public class FilterConfig {
- @Bean
- public FilterRegistrationBean<SimpleCorsFilter> corsFilter() {
- FilterRegistrationBean<SimpleCorsFilter> registrationBean = new FilterRegistrationBean<>();
- registrationBean.setFilter(new SimpleCorsFilter());
- registrationBean.addUrlPatterns("/*");
- return registrationBean;
- }
- }
复制代码 4. 利用拦截器(Interceptor)
如果须要更复杂的CORS逻辑,你可以创建一个拦截器来处理惩罚CORS哀求。拦截器答应你在哀求处理惩罚之前或之后添加逻辑。
- import org.springframework.web.servlet.HandlerInterceptor;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- public class CorsInterceptor implements HandlerInterceptor {
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
- response.setHeader("Access-Control-Allow-Origin", "http://example.com");
- response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
- response.setHeader("Access-Control-Allow-Headers", "*");
- return true;
- }
- }
复制代码 然后,你须要在设置类中注册这个拦截器:
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
- import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
- @Configuration
- public class WebConfig implements WebMvcConfigurer {
- @Autowired
- private CorsInterceptor corsInterceptor;
- @Override
- public void addInterceptors(InterceptorRegistry registry) {
- registry.addInterceptor(corsInterceptor).addPathPatterns("/**");
- }
- }
复制代码
- 当设置allowedHeaders("*")时,实际上欣赏器会发送实际哀求头而不是*。出于安全思量,最好明确指定答应的头部。
- 在生产情况中,确保不要过于宽松地设置CORS,只答应须要的源和方法。
- 如果你的应用摆设在署理服务器反面(如Nginx或Apache),大概还须要在署理服务器上设置CORS。
5.相应体(Response Body)来设置CORS
固然这种方式不如前面提到的几种方法直接和常用,但在某些特别场景下,你大概须要手动控制相应头来实现跨域。
详细实现时,你可以在Controller的方法中,通过HttpServletResponse对象来设置Access-Control-Allow-Origin等CORS干系的HTTP头。
- import javax.servlet.http.HttpServletResponse;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RestController;
- @RestController
- public class MyController {
- @GetMapping("/data")
- public String getData(HttpServletResponse response) {
- response.setHeader("Access-Control-Allow-Origin", "http://example.com");
- response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
- response.setHeader("Access-Control-Allow-Headers", "*");
- response.setHeader("Access-Control-Max-Age", "3600");
- return "Data";
- }
- }
复制代码 须要留意的是,手动设置相应头的方式相对繁琐,且容易遗漏某些须要的头信息,导致CORS设置不完备。因此,在实际开发中,保举利用前面提到的方法,它们更为直接且易于管理。
别的,如果你正在利用Spring Security,还须要确保Spring Security的设置不会制止跨域哀求的处理惩罚。在某些情况下,你大概须要在Spring Security的设置中答应特定的CORS哀求。
6.通过自界说ResponseBodyAdvice
ResponseBodyAdvice是Spring MVC提供的一个接口,答应你在Controller方法返反相应体之前对其举行修改。固然它本身不是专为CORS筹划的,但你可以利用它在返反相应之前添加CORS干系的HTTP头。
下面是一个简单的示例,展示了怎样通过实现ResponseBodyAdvice接口来添加CORS头:
- import org.springframework.core.MethodParameter;
- import org.springframework.http.MediaType;
- import org.springframework.http.server.ServerHttpRequest;
- import org.springframework.http.server.ServerHttpResponse;
- import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
- import java.lang.reflect.Type;
- public class CorsResponseBodyAdvice implements ResponseBodyAdvice<Object> {
- @Override
- public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
- return true;
- }
- @Override
- public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
- Class<? extends HttpMessageConverter<?>> selectedConverterType,
- ServerHttpRequest request, ServerHttpResponse response) {
- response.getHeaders().set("Access-Control-Allow-Origin", "http://example.com");
- response.getHeaders().set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
- response.getHeaders().set("Access-Control-Allow-Headers", "*");
- return body;
- }
- }
复制代码 然后,你须要在Spring Boot的设置中注册这个ResponseBodyAdvice:
- import org.springframework.context.annotation.Configuration;
- import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
- @Configuration
- public class WebConfig implements WebMvcConfigurer {
- @Override
- public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
- converters.forEach(converter -> {
- if (converter instanceof MappingJackson2HttpMessageConverter) {
- ((MappingJackson2HttpMessageConverter) converter).setResponseBodyAdvice(new CorsResponseBodyAdvice());
- }
- });
- }
- }
复制代码 这种方法的优点在于它可以全局地应用于全部Controller的相应,而无需在每个Controller或方法上单独设置。然而,它同样也有一些范围性,比如你大概须要手动处理惩罚一些CORS的细节,而且这种方式不如利用Spring提供的CORS支持那么直接和机动。
在选择办理方案时,应该根据项目标详细需求和团队的偏好来衡量各种方法的优缺点。如果项目中有大量的Controller须要处理惩罚跨域哀求,而且盼望有一个同一且全局的办理方案,那么利用WebMvcConfigurer或CorsFilter
大概是更好的选择。如果只须要在特定的Controller或方法上处理惩罚跨域哀求,那么利用@CrossOrigin注解大概更为简单直接。
|