前后端分离 开发环境通过CORS实现跨域联调
通过JSONP实现跨域已是老生常谈,JSONP跨域限制多,最近了解了一下CORS。
参考:
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS
http://newhtml.net/using-cors/
https://www.w3.org/TR/2013/PR-cors-20131205/
CORS是W3c的一个工作草案,定义了在跨域访问资源时浏览器和服务器应该如何沟通。CORS背后的基本思想是使用自定义的HTTP头部让浏览器和服务器进行沟通,从而决定请求或响应成功与否。
比如一个简单的使用get或post发送的请求,它没有自定义的头部,而主体内容是text/plain。在发送该请求时,需要给它附加一个额外的Origin头部,其中包含请求页面的源信息(协议,域名和端口),以便服务器根据这个头部信息来决定是否给予相应。
Origin:http://www.nczonline.net
如果服务器认为这个请求可以接受,就在Access-Control-Allow-Origin头部中回发相同的源信息(如果是公共资源,可以回发***)。
Access-Control-Allow-Origin://www.nczonline.net
如果没有这个头部,或者有这个头部但源信息不匹配,浏览器就会驳回请求。正常情况下,浏览器会处理请求。注意请求和响应都不包含cookies信息。
同源策略:是浏览器最核心也最基本的安全功能;同源指的是:同协议,同域名和同端口。精髓:认为自任何站点装载的信赖内容是不安全的。当被浏览器半信半疑的脚本运行在沙箱时,它们应该只被允许访问来自同一站点的资源,而不是那些来自其它站点可能怀有恶意的资源;参考:JavaScript 的同源策略
JSON & JSONP:JSON 是一种基于文本的数据交换方式,或者叫做数据描述格式。JSONP是资料格式JSON的一种“使用模式”,可以让网页从别的网域要资料,由于同源策略,一般来说位于server1.example.com的网页无法与不是 server1.example.com的服务器沟通,而HTML的script元素是一个例外。利用script元素的这个开放策略,网页可以得到从其他来源动态产生的JSON资料,而这种使用模式就是所谓的JSONP
对比JSONP和CORS发现以下几点区别:
- JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求
- 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理
- JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS
最近做项目,前端使用vue,后端使用spring-boot,前后端完全分离,开发联调的时候碰到了跨域问题,用了CORS解决跨域。
查看了spring中几个cors相关的类:
org.springframework.web.cors.CorsConfiguration
org.springframework.web.servlet.config.annotation.CorsRegistry
org.springframework.web.cors.DefaultCorsProcessor
其中DefaultCorsProcessor源码如下:
- /*
- * Copyright 2002-2016 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.springframework.web.cors;
- import java.io.IOException;
- import java.nio.charset.Charset;
- import java.util.ArrayList;
- import java.util.List;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- import org.springframework.http.HttpHeaders;
- import org.springframework.http.HttpMethod;
- import org.springframework.http.HttpStatus;
- import org.springframework.http.server.ServerHttpRequest;
- import org.springframework.http.server.ServerHttpResponse;
- import org.springframework.http.server.ServletServerHttpRequest;
- import org.springframework.http.server.ServletServerHttpResponse;
- import org.springframework.util.CollectionUtils;
- import org.springframework.web.util.WebUtils;
- /**
- * The default implementation of {@link CorsProcessor}, as defined by the
- * <a href="http://www.w3.org/TR/cors/">CORS W3C recommendation</a>.
- *
- * <p>Note that when input {@link CorsConfiguration} is {@code null}, this
- * implementation does not reject simple or actual requests outright but simply
- * avoid adding CORS headers to the response. CORS processing is also skipped
- * if the response already contains CORS headers, or if the request is detected
- * as a same-origin one.
- *
- * @author Sebastien Deleuze
- * @author Rossen Stoyanchev
- * @since 4.2
- */
- public class DefaultCorsProcessor implements CorsProcessor {
- private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
- private static final Log logger = LogFactory.getLog(DefaultCorsProcessor.class);
- @Override
- @SuppressWarnings("resource")
- public boolean processRequest(CorsConfiguration config, HttpServletRequest request, HttpServletResponse response)
- throws IOException {
- if (!CorsUtils.isCorsRequest(request)) {
- return true;
- }
- ServletServerHttpResponse serverResponse = new ServletServerHttpResponse(response);
- if (responseHasCors(serverResponse)) {
- logger.debug("Skip CORS processing: response already contains \"Access-Control-Allow-Origin\" header");
- return true;
- }
- ServletServerHttpRequest serverRequest = new ServletServerHttpRequest(request);
- if (WebUtils.isSameOrigin(serverRequest)) {
- logger.debug("Skip CORS processing: request is from same origin");
- return true;
- }
- boolean preFlightRequest = CorsUtils.isPreFlightRequest(request);
- if (config == null) {
- if (preFlightRequest) {
- rejectRequest(serverResponse);
- return false;
- }
- else {
- return true;
- }
- }
- return handleInternal(serverRequest, serverResponse, config, preFlightRequest);
- }
- private boolean responseHasCors(ServerHttpResponse response) {
- try {
- return (response.getHeaders().getAccessControlAllowOrigin() != null);
- }
- catch (NullPointerException npe) {
- // SPR-11919 and https://issues.jboss.org/browse/WFLY-3474
- return false;
- }
- }
- /**
- * Invoked when one of the CORS checks failed.
- * The default implementation sets the response status to 403 and writes
- * "Invalid CORS request" to the response.
- */
- protected void rejectRequest(ServerHttpResponse response) throws IOException {
- response.setStatusCode(HttpStatus.FORBIDDEN);
- response.getBody().write("Invalid CORS request".getBytes(UTF8_CHARSET));
- }
- /**
- * Handle the given request.
- */
- protected boolean handleInternal(ServerHttpRequest request, ServerHttpResponse response,
- CorsConfiguration config, boolean preFlightRequest) throws IOException {
- String requestOrigin = request.getHeaders().getOrigin();
- String allowOrigin = checkOrigin(config, requestOrigin);
- HttpMethod requestMethod = getMethodToUse(request, preFlightRequest);
- List<HttpMethod> allowMethods = checkMethods(config, requestMethod);
- List<String> requestHeaders = getHeadersToUse(request, preFlightRequest);
- List<String> allowHeaders = checkHeaders(config, requestHeaders);
- if (allowOrigin == null || allowMethods == null || (preFlightRequest && allowHeaders == null)) {
- rejectRequest(response);
- return false;
- }
- HttpHeaders responseHeaders = response.getHeaders();
- responseHeaders.setAccessControlAllowOrigin(allowOrigin);
- responseHeaders.add(HttpHeaders.VARY, HttpHeaders.ORIGIN);
- if (preFlightRequest) {
- responseHeaders.setAccessControlAllowMethods(allowMethods);
- }
- if (preFlightRequest && !allowHeaders.isEmpty()) {
- responseHeaders.setAccessControlAllowHeaders(allowHeaders);
- }
- if (!CollectionUtils.isEmpty(config.getExposedHeaders())) {
- responseHeaders.setAccessControlExposeHeaders(config.getExposedHeaders());
- }
- if (Boolean.TRUE.equals(config.getAllowCredentials())) {
- responseHeaders.setAccessControlAllowCredentials(true);
- }
- if (preFlightRequest && config.getMaxAge() != null) {
- responseHeaders.setAccessControlMaxAge(config.getMaxAge());
- }
- response.flush();
- return true;
- }
- /**
- * Check the origin and determine the origin for the response. The default
- * implementation simply delegates to
- * {@link org.springframework.web.cors.CorsConfiguration#checkOrigin(String)}.
- */
- protected String checkOrigin(CorsConfiguration config, String requestOrigin) {
- return config.checkOrigin(requestOrigin);
- }
- /**
- * Check the HTTP method and determine the methods for the response of a
- * pre-flight request. The default implementation simply delegates to
- * {@link org.springframework.web.cors.CorsConfiguration#checkOrigin(String)}.
- */
- protected List<HttpMethod> checkMethods(CorsConfiguration config, HttpMethod requestMethod) {
- return config.checkHttpMethod(requestMethod);
- }
- private HttpMethod getMethodToUse(ServerHttpRequest request, boolean isPreFlight) {
- return (isPreFlight ? request.getHeaders().getAccessControlRequestMethod() : request.getMethod());
- }
- /**
- * Check the headers and determine the headers for the response of a
- * pre-flight request. The default implementation simply delegates to
- * {@link org.springframework.web.cors.CorsConfiguration#checkOrigin(String)}.
- */
- protected List<String> checkHeaders(CorsConfiguration config, List<String> requestHeaders) {
- return config.checkHeaders(requestHeaders);
- }
- private List<String> getHeadersToUse(ServerHttpRequest request, boolean isPreFlight) {
- HttpHeaders headers = request.getHeaders();
- return (isPreFlight ? headers.getAccessControlRequestHeaders() : new ArrayList<String>(headers.keySet()));
- }
- }
Spring 中对 CORS 规则的校验,都是通过委托给 DefaultCorsProcessor实现的。
DefaultCorsProcessor 处理过程如下:
首先 CorsUtils.isCorsRequest(request) 通过request请求头部是否包含origin头部来判断是否是cors请求,若不包含则返回true,若包含则继续往下执行。
然后 responseHasCors(serverResponse) 判断response头部是否已经设置了Access-Control-Allow-Origin,如果已经设置了也不用再进行cors处理,返回true,若没有设置Access-Control-Allow-Origin则继续往下执行。
再判断是否同源,判断request带的origin(请求的来源域)和forward(需要请求的资源)否一致(协议,域名,端口号一致),一致则证明不是跨域访问返回true,不一致则继续往下执行。
最后检查是否是预请求,服务端是否有跨域配置,没有跨域配置且不是预请求则返回true,没有跨域配置但请求是预请求则拒绝该请求返回false。
若有跨域配置,则检查请求中origin 是否合法,method 是否合法,header是否合法,如果全部合法,则在 response header中添加响应的字段,并交给负责该请求的类处理,如果不合法,则拒绝该请求。
spring-boot项目中通过继承org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter,重写addCorsMappings方法支持跨域
- @Override
- public void addCorsMappings(CorsRegistry registry) {
- registry.addMapping("/**")
- .allowedOrigins("*")
- .allowCredentials(true)
- .allowedMethods("GET", "POST", "DELETE", "PUT")
- .maxAge(3600);
- }
另外后端通过swagger提供接口参数说明及测试接口,浏览器也需要配置支持跨域,以chrome为例,设置支持跨域方式:
右键点击chrome浏览器快捷方式,在属性窗口中加入如下配置:--args --disable-web-security --user-data-dir=E:\chromeDevData,然后重启浏览器。
会发现浏览器上方有如下提示,证明跨域配置生效。
前后端分离 开发环境通过CORS实现跨域联调的更多相关文章
- 前后端分离跨域 关于前后端分离开发环境下的跨域访问问题(angular proxy=>nginx )
前后端分离后遇到了跨域访问的问题: angular1中使用proxy很麻烦,最后还是失败结束:最后总结3种方法如下: 本人使用的第一种方法,只是开发环境下使用很方便! 1:禁掉谷歌的安全策略(Turn ...
- Nginx完美解决前后端分离端口号不同导致的跨域问题
笔者在做前后端分离系统时,出现了很多坑,比如前后端的url域名相同,但是端口号不同.例如前端页面为:http://127.0.0.1/ , 后端api根路径为 http://127.0.0.1:888 ...
- 如何利用vue和php做前后端分离开发?
新手上路,前端工程师,刚毕业参加工作两个月,上面让我用vue搭建环境和php工程师一起开发,做前后端分离,然而我只用过简单的vue做一些小组件的经验,完全不知道怎样和php工程师配合,ps: php那 ...
- 超简单工具puer——“低碳”的前后端分离开发
本文由作者郑海波授权网易云社区发布. 前几天,跟一同事(MIHTool作者)讨教了一下开发调试工具.其实个人觉得相较于定制一个类似MIHTool的Hybrid App容器,基于长连的B/S架构的工具其 ...
- 【坑】前后端分离开发中 跨域问题以及前台不带cookie的问题
文章目录 前言 跨域问题 cookie问题 拦截器导致的跨域问题 后记 前言 场景一: 前台哒哒哒的点击页面,发送请求,但是后台服务器总是没有回应,后台接口虽打了断点,但是根本进不到断点处: 前端:我 ...
- Web前后端分离开发(CRUD)及其演变概括
今天学习了前后端分离开发模式又从网上查了一些资料就写了一篇博客分享: 一.为什么分离前后端 1.1早期开发 1.2后段为主mvc模式 1.2.1Structs框架介绍 1.2.2Spring mcv开 ...
- vue+mockjs 模拟数据,实现前后端分离开发
在项目中尝试了mockjs,mock数据,实现前后端分离开发. 关于mockjs,官网描述的是 1.前后端分离 2.不需要修改既有代码,就可以拦截 Ajax 请求,返回模拟的响应数据. 3.数据类型丰 ...
- 基于RAP(Mock)实现前后端分离开发
看看RAP的官方定义: 什么是RAP? (Rigel API Platform) 在前后端分离的开发模式下,我们通常需要定义一份接口文档来规范接口的具体信息.如一个请求的地址.有几个参数.参数名称及类 ...
- laravel5.7 前后端分离开发 实现基于API请求的token认证
最近在学习前后端分离开发,发现 在laravel中实现前后台分离是无法无法使用 CSRF Token 认证的.因为 web 请求的用户认证是通过Session和客户端Cookie的实现的,而前后端分离 ...
随机推荐
- android webview setcookie 设置cookie
CookieSyncManager.createInstance(mWebView.getContext()); CookieManager cookieManager = CookieManager ...
- 【EasyUI学习-1】MyEclipse+easyui学习官方Demo
介绍 easyui的介绍,网上很多,这里就不进行介绍了. easyUI获取 官网: http://www.jeasyui.com/ 下载地址:http://www.jeasyui.com/downl ...
- C# webBrowser 获取元素class属性值
// he 是HtmlElement对象 // GetAttribute("class") 一直取空值 he.GetAttribute("className")
- Appium+python自动化8-Appium Python API
Appium+python自动化8-AppiumPython API 前言: Appium Python API全集,不知道哪个大神整理的,这里贴出来分享给大家. 1.contexts conte ...
- [datatable]关于在DataTable中执行DataTable.Select("条件")返回DataTable的解决方法
-- :09关于在DataTable中执行DataTable.Select("条件")返回DataTable的解决方法 在实际编程工程中,常常遇到这样的情况:DataTable并不 ...
- 【BZOJ】1101 [POI2007]Zap(莫比乌斯反演)
题目 传送门:QWQ 分析 莫比乌斯反演. 还不是很熟练qwq 代码 //bzoj1101 //给出a,b,d,询问有多少对二元组(x,y)满足gcd(x,y)=d.x<=a,y<=b # ...
- PHP mysqli的prepare准备语句使用说明
mysqli对prepare的支持对于大访问量的网站是很有好处的,它极大地降低了系统开销,而且保证了创建查询的稳定性和安全性.prepare准备语句分为绑定参数和绑定结果,下面将会一一介绍! (1)绑 ...
- ganglia
A.lamp界面快速搭建---------------------------------------------------------------------------------------- ...
- Linux 配置TomCat 项目三大步骤
一: 安装 JRE 01: 下载 server-jre 安装包 => http://www.oracle.com/technetwork/java/javase/downloads/serve ...
- 9 个Java 异常处理的规则
在 Java 中,异常处理是个很麻烦的事情.初学者觉得它很难理解,甚至是经验丰富的开发者也要花费很长时间决定异常是要处理掉和抛出. 所以很多开发团队约定一些原则处理异常.如果你是一个团队的新成员,你可 ...