<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
對於跨域,相信同學們都有所瞭解。前端的跨域的若干種方式,大家也都知道,什麼 JSONP,iframe+domain 等等。但是我們今天的主題,不是前端跨域,而是後端跨域。
一旦提及到跨域,就會想到同源策略,那我們就先來回顧跨域和同源策略。
首先,我們要了解什麼是跨域請求。簡單來說,當一臺伺服器資源從另一臺伺服器(不同 的域名或者埠)請求一個資源或者介面,就會發起一個跨域 HTTP 請求。
舉個簡單的例子:
從http://aaa.com/index.html,傳送一個 Ajax 請求,請求地址是 http://bbb.com/下面的一個介面,這就是發起了一個跨域請求。在不做任何處理的情況下,這個跨域請求是無法被成功請求的,因為瀏覽器基於同源策略 會對跨域請求做一定的限制。那什麼又是同源策略呢?
首先大家要知道同源策略發生的場景——瀏覽器中,什麼意思呢?如果不是瀏覽器的話, 就不會受到同源策略的影響。也就是說,兩個伺服器直接進行跨域請求是可以進行資料請求的。這也就為我們接下來的後端跨域埋下一下小伏筆。 同源策略的目的是什麼呢?同源策略限制了從同一個源載入的檔案或者指令碼如何與來自另 一個源的資源進行互動。這是一個用於隔離潛在惡意檔案的重要安全機制。
大家都知道,一個域名請求地址的組成是:協定+域名+埠號+請求資源地址 , 當協定、域名、埠號中任意一個不相同時 , 都算作不同源(必須是域名完全相同,比如說 a.example.com 和 b.example.com 這兩個域名。
雖然它們的頂級域名和二級域名(均為 example.com)都相同,但是三級域名(a 和 b)不相同,所以也不能算作域名相同)。
如果不同時滿足這上面三個條件,那就不符合瀏覽器的同源策略。 需要注意的是,不是所有的互動都會被同源策略攔截下來,下面兩種互動就不會觸發同源策略:
跨域寫操作(Cross-origin writes)
跨域資源嵌入(Cross-origin embedding)
這裡你或許有個疑問:請求跨域了,那麼請求到底發出去沒有?
跨域並不是請求發不出去,請求能發出去,伺服器端能收到請求並正常返回結果,只是結果被瀏覽器攔截了。
你可能會疑問明明通過表單的方式可以發起跨域請求,為什麼 Ajax 就不會?因為歸根結底,跨域是為了阻止使用者讀取到另一個域名下的內容,Ajax 可以獲取響應,瀏覽器認為這不安全,所以攔截了響應。但是表單並不會獲取新的內容,所以可以發起跨域請求。
同時也說明了跨域並不能完全阻止 CSRF,因為請求畢竟是發出去了。
跨域就是通過某些手段來繞過同源策略限制,實現不同伺服器之間通訊的效果。方法有很多 ,大致分為兩類:
常見的解決方案有:
利用 <script> 標籤沒有跨域限制的漏洞,網頁可以得到從其他來源動態產生的 JSON 資料。JSONP請求一定需要對方的伺服器做支援才可以。
JSONP和AJAX相同,都是使用者端向伺服器端傳送請求,從伺服器端獲取資料的方式。但AJAX屬於同源策略,JSONP屬於非同源策略(跨域請求)
JSONP優點是簡單相容性好,可用於解決主流瀏覽器的跨域資料存取的問題。缺點是僅支援get方法具有侷限性,不安全可能會遭受XSS攻擊。
CORS(Cross-origin resource sharing),跨域資源共用。CORS 其實是瀏覽器制定的一個規範,瀏覽器會自動進行 CORS 通訊,它的實現則主要在伺服器端,它通過一些 HTTP Header 來限制可以存取的域,例如頁面 A 需要存取 B 伺服器上的資料,如果 B 伺服器 上宣告了允許 A 的域名存取,那麼從 A 到 B 的跨域請求就可以完成。對於那些會對伺服器資料產生副作用的 HTTP 請求,瀏覽器會使用 OPTIONS 方法發起 一個預檢請求(preflight request),從而可以獲知伺服器端是否允許該跨域請求,服 務器端確認允許後,才會發起實際的請求。在預檢請求的返回中,伺服器端也可以告知客 戶端是否需要身份認證資訊。我們只需要設定響應頭,即可進行跨域請求。
雖然設定 CORS 和前端沒什麼關係,但是通過這種方式解決跨域問題的話,會在傳送請求時出現兩種情況,分別為簡單請求和複雜請求。
簡單請求:
只要同時滿足以下兩大條件,就屬於簡單請求:
1)使用GET、HEAD、POST方法之一;
2)Content-Type 的值僅限於:text/plain、multipart/form-data、application/x-www-form-urlencoded,請求中的任意 XMLHttpRequestUpload 物件均沒有註冊任何事件監聽器; XMLHttpRequestUpload 物件可以使用 XMLHttpRequest.upload 屬性存取;
複雜請求:
不符合以上條件的請求就肯定是複雜請求了。 複雜請求的CORS請求,會在正式通訊之前,增加一次HTTP查詢請求,稱為"預檢"請求,該請求是 option 方法的,通過該請求來知道伺服器端是否允許跨域請求。
我們用PUT向後臺請求時,屬於複雜請求,後臺需被請求的Servlet中新增Header設定,Access-Control-Allow-Origin這個Header在W3C標準裡用來檢查該跨域請求是否可以被通過,如果值為*則表明當前頁面可以跨域存取。預設的情況下是不允許的。
一般我們可以寫一個過濾器:
@WebFilter(filterName = "corsFilter", urlPatterns = "/*", initParams = {@WebInitParam(name = "allowOrigin", value = "*"), @WebInitParam(name = "allowMethods", value = "GET,POST,PUT,DELETE,OPTIONS"), @WebInitParam(name = "allowCredentials", value = "true"), @WebInitParam(name = "allowHeaders", value = "Content-Type,X-Token")}) public class CorsFilter implements Filter { private String allowOrigin; private String allowMethods; private String allowCredentials; private String allowHeaders; private String exposeHeaders; @Override public void init(FilterConfig filterConfig) throws ServletException { allowOrigin = filterConfig.getInitParameter("allowOrigin"); allowMethods = filterConfig.getInitParameter("allowMethods"); allowCredentials = filterConfig.getInitParameter("allowCredentials"); allowHeaders = filterConfig.getInitParameter("allowHeaders"); exposeHeaders = filterConfig.getInitParameter("exposeHeaders"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; if (!StringUtils.isEmpty(allowOrigin)) { if(allowOrigin.equals("*")){ // 設定哪個源可以存取 response.setHeader("Access-Control-Allow-Origin", allowOrigin); }else{ List<String> allowOriginList = Arrays.asList(allowOrigin.split(",")); if (allowOriginList != null && allowOriginList.size() > 0) { String currentOrigin = request.getHeader("Origin"); if (allowOriginList.contains(currentOrigin)) { response.setHeader("Access-Control-Allow-Origin", currentOrigin); } } } } if (!StringUtils.isEmpty(allowMethods)) { //設定哪個方法可以存取 response.setHeader("Access-Control-Allow-Methods", allowMethods); } if (!StringUtils.isEmpty(allowCredentials)) { // 允許攜帶cookie response.setHeader("Access-Control-Allow-Credentials", allowCredentials); } if (!StringUtils.isEmpty(allowHeaders)) { // 允許攜帶哪個頭 response.setHeader("Access-Control-Allow-Headers", allowHeaders); } if (!StringUtils.isEmpty(exposeHeaders)) { // 允許攜帶哪個頭 response.setHeader("Access-Control-Expose-Headers", exposeHeaders); } filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { } }
大功告成,現在前端就可以跨域獲取後臺的資料了,正如我們上面所說的,後端是實現 CORS 通訊的關鍵。
如果你的SpringBoot版本在2.0以上,以下程式碼設定即可完美解決你的前後端跨域請求問題:
@Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); final CorsConfiguration corsConfiguration = new CorsConfiguration(); /*是否允許請求帶有驗證資訊*/ corsConfiguration.setAllowCredentials(true); /*允許存取的使用者端域名*/ corsConfiguration.addAllowedOrigin("*"); /*允許伺服器端存取的使用者端請求頭*/ corsConfiguration.addAllowedHeader("*"); /*允許存取的方法名,GET POST等*/ corsConfiguration.addAllowedMethod("*"); urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration); return new CorsFilter(urlBasedCorsConfigurationSource); } }
或者使用WebMvcConfigurationSupport,實現方式有很多,感興趣的可以自行研究。
這個方法僅對Java有用。springboot中,在Controller類上新增一個 @CrossOrigin(origins ="*") 註解就可以實現對當前controller 的跨域存取了,當然這個標籤也可以加到方法上,或者直接加到入口類上對所有介面進行跨域處理,注意這個註解只在JDK1.8版本以上才起作用。
服務閘道器(zuul)又稱路由中心,用來統一存取所有api介面,維護服務。
Spring Cloud Zuul通過與Spring Cloud Eureka的整合,實現了對服務範例的自動化維護,所以在使用服務路由設定的時候,我們不需要向傳統路由設定方式那樣去指定具體的服務範例地址,只需要通過Ant模式組態檔引數即可
實現原理:同源策略是瀏覽器需要遵循的標準,而如果是伺服器向伺服器請求就無需遵循同源策略。這樣的話,我們可以讓伺服器替我們傳送一個請求,請求其他伺服器下面的資料。然後我們的頁面存取當前伺服器下的介面就沒有跨域問題了。
代理伺服器,需要做以下幾個步驟:
實現原理類似於Node中介軟體代理,需要你搭建一箇中轉nginx伺服器,用於轉發請求。
使用nginx反向代理實現跨域,是最簡單的跨域方式。只需要修改nginx的設定即可解決跨域問題,支援所有瀏覽器,支援session,不需要修改任何程式碼,並且不會影響伺服器效能。
實現思路:通過nginx設定一個代理伺服器做跳板機,反向代理存取domain2介面,並且可以順便修改cookie中domain資訊,方便當前域cookie寫入,實現跨域登入。
將nginx目錄下的nginx.conf修改如下:
// proxy伺服器 server { listen 81; server_name www.domain1.com; location / { proxy_pass http://www.domain2.com:8080; #反向代理 proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie裡域名 index index.html index.htm; # 當用webpack-dev-server等中介軟體代理介面存取nignx時,此時無瀏覽器參與,故沒有同源限制,下面的跨域設定可不啟用 add_header Access-Control-Allow-Origin http://www.domain1.com; #當前端只跨域不帶cookie時,可為* add_header Access-Control-Allow-Credentials true; add_header Access-Control-Allow-Methods GET, POST, OPTIONS; add_header Access-Control-Allow-Headers *; } }
這樣我們的前端代理只要存取 http:www.domain1.com:81/*就可以了。
以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45