赞
踩
为了避免 XSS、CSRF 等攻击,浏览器使用同源策略对跨域 HTTP 请求进行限制。对于前后端分离的项目,如果前端项目与后端项目部署在两个不同的域下,那么前端访问后端时会产生跨域问题。
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS
http://www.ruanyifeng.com/blog/2016/04/cors.html
跨域资源共享是一个 W3C 标准,它基于 HTTP 标头机制,新增了一组 HTTP 标头字段。CORS 需要浏览器和服务器同时支持,它允许浏览器向跨域服务器发送请求,并由服务器决定哪些源站有权限跨域访问资源。因此,实现 CORS 通信的关键是服务器
目前,所有主流浏览器(IE10及以上)使用 XMLHttpRequest 对象都可支持该功能。CORS 通信过程由浏览器自动完成,浏览器一旦发现 AJAX 请求跨源,会自动在头信息中增加 Origin 字段,说明本次请求来自哪个源(协议+域名+端口)。
浏览器将 CORS 请求分成两类:简单请求和需预检的请求。
假如站点 https://foo.example
的网页应用想要访问 https://bar.other
的资源
浏览器一旦发现 AJAX 请求跨域,就会自动在请求头信息中增加 Origin 字段
GET /cors HTTP/1.1
...
Origin: https://foo.example
服务端返回
Access-Control-Allow-Origin: *
表明该资源可以被任意外源访问。
使用 Origin 和 Access-Control-Allow-Origin 就能完成最简单的访问控制。
如果 https://bar.other
的资源持有者想限制他的资源只能通过 https://foo.example
来访问,服务端必须明确 Access-Control-Allow-Origin
的值,而不能使用通配符*
:
Access-Control-Allow-Origin: https://foo.example
简单请求需要满足以下两个条件:
请求方法是以下三种方法之一:HEAD、GET、POST。
HTTP的头信息不超出以下几种字段:Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type:只限于三个值 application/x-www-form-urlencoded、multipart/form-data、text/plain。
需预检的请求必须首先使用 OPTIONS 方法发起一个**预检请求(preflight request)**到服务器,获知服务器是否允许该实际请求。
OPTIONS /doc HTTP/1.1
...
Origin: https://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
实际的 POST
请求不会携带 Access-Control-Request-*
标头,它们仅用于 OPTIONS
请求。
标头字段 Access-Control-Request-Method
告知服务器,实际请求将使用 POST
方法。
标头字段 Access-Control-Request-Headers
告知服务器,实际请求将携带两个自定义请求标头字段:X-PINGOTHER
与 Content-Type
。
服务器据此决定,该实际请求是否被允许。
服务端返回
Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
服务器响应中,标头字段 Access-Control-Allow-Origin: https://foo.example
,限制请求的源。
标头字段 Access-Control-Allow-Methods
表明服务器允许客户端使用 POST
和 GET
方法发起请求。
标头字段 Access-Control-Allow-Headers
表明服务器允许请求中携带字段 X-PINGOTHER
与 Content-Type
。
标头字段 Access-Control-Max-Age
给定了该预检请求可供缓存的时间长短,单位为秒,默认值是 5 秒。在有效时间内,浏览器无须为同一请求再次发起预检请求。以上例子中,该响应的有效时间为 86400 秒,也就是 24 小时。注意,浏览器自身维护了一个最大有效时间,如果该标头字段的值超过了最大有效时间,将不会生效。
预检请求完成之后,发送实际请求。
当 CORS 请求需要携带 cookie 时,需要服务端配置 Access-Control-Allow-Credentials 头信息,前端也需要设置 withCredentials。
Origin: https://foo.example
Cookie: pageAccess=2
服务端返回
Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Credentials: true
如果服务器端的响应中未携带 Access-Control-Allow-Credentials: true
,浏览器将不会把响应内容返回给请求的发送者。
https://zhuanlan.zhihu.com/p/424835912
Springboot 处理客户端请求的流程如图所示:
请求从浏览器发出,最先通过过滤器链,然后通过Dispatcher Servlet,然后通过拦截器,最后才到达处理请求的控制器上。我们只要让 Springboot 在收到 OPTIONS 试探请求时,过滤器或者在拦截器上给浏览器返回它需要的那几个header信息就可以了。
细粒度控制
import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @RestController // 写在类上对类中所有方法有效 @CrossOrigin(origins = "允许跨域的域名", allowCredentials = "true", allowedHeaders = "*", maxAge = 1800) public class TestController { // 写在方法上对当前方法有效 @CrossOrigin(origins = "http://localhost:8081", allowCredentials = "true", allowedHeaders = "*") @RequestMapping(value = "/api/test", method = RequestMethod.POST) public ResponseEntity test() { return ResponseEntity.ok("test"); } }
使用此方法配置之后再使用自定义拦截器时跨域相关配置就会失效。
原因是请求到来时会先进入拦截器中,而不是进入 Mapping 映射中,所以返回的头信息中并没有配置的跨域信息。浏览器就会报跨域异常。
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 允许跨域请求的路径,"/**" 表示全部
.allowedOrigins("*") // 允许跨域请求的来源,"*" 表示所有域名来源
.allowedHeaders("*") // 允许跨域请求可携带的 header,"*" 表示所有 header。CORS 请求时,XMLHttpRequest 对象的 getResponseHeader() 方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在 Access-Control-Expose-Headers 里面指定
.allowedMethods("*") // 允许跨域请求的方法,"*" 表示所有
.allowCredentials(true) // 是否允许发送 cookie,true-允许 false-不允许,默认false
.maxAge(86400); // 预检间隔时间,单位为秒
}
}
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedHeaders("*")
.allowedMethods("*")
.allowCredentials(true)
.maxAge(86400);
}
}
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; @Configuration public class CorsConfig { private CorsConfiguration corsConfig() { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.addAllowedOrigin("*"); corsConfiguration.addAllowedHeader("*"); corsConfiguration.addAllowedMethod("*"); corsConfiguration.setAllowCredentials(true); corsConfiguration.setMaxAge(3600L); return corsConfiguration; } @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", corsConfig()); return new CorsFilter(source); } }
静态网站:不需要服务器端处理,服务器只需要检索请求的文件并将它们交付给客户端。
动态网站:需要数据库或服务器。如果用户可以与它进行交互,那么它就是一个动态网站。
跨站脚本攻击是代码注入的一种,恶意攻击者在 Web 页面里注入恶意 Script 代码,其他用户在浏览网页时,嵌入其中的 Script 代码会被执行,从而达到恶意攻击用户的目的。
https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy
统一资源标识符(Uniform Resource Identifier,URI)
统一资源定位器(Uniform Resource Locator, URL)
URL:协议 - 子域名 - 主域名 - 端口号 - 请求资源地址
同源:如果两个 URL 的协议、域名、端口号三者都相同,就称这两个URL同源。
跨源 HTTP 请求的一个例子:运行在 https://domain-a.com
的 JavaScript 代码使用 XMLHttpRequest 来发起一个到 https://domain-b.com/data.json
的请求。
出于安全性,浏览器限制脚本内发起的跨源 HTTP 请求。
例如,XMLHttpRequest
遵循同源策略,意味着使用这类 API 的 Web 应用程序只能从加载应用程序的同一个域请求 HTTP 资源,除非响应报文包含了正确 CORS 响应头。
使用 Nginx 反向代理实现跨域,是最简单的跨域方式。只需要修改 Nginx 的配置即可解决跨域问题,支持所有浏览器,支持 session,不需要修改任何代码,并且不会影响服务器性能。
JSONP 只支持 GET 请求。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。