記一次springMVC的跨域解決方案

日期:2019年5月18日html

事情緣由:因爲微信小程序的開發只有測試環境,然後臺提供接口的環境是開發環境;兩個環境的域名不一樣,致使前端開發產生了跨域問題;前端

理論概念:nginx

  一、同源策略:同源策略是瀏覽器的安全基石,存儲在瀏覽器中的數據(cookie)必須在同域(相同域名)下才能任意的讀取,而非同域下的瀏覽器資源是不容許任意操做的。即同源策略下,非同源的網站之間不能發送Ajax請求;若是沒有同源策略,會致使瀏覽器中的數據能夠被任何來源請求進行訪問與資源操做,存儲在瀏覽器中的數據就沒有任何安全可言;git

      二、域名:相同域名(www.baidu .com;全部的請求都來自於這個域名下,即爲同域下);github

                   不一樣域名(www.baidu.com,www.google.com;來自google的請求不能直接操做baidu域下的資源);web

      三、同源:兩個頁面的【協議】、【端口(若是指定了)】、【主機】都相同,則這兩個頁面具備相同的源。有一個元素不一樣,則是不一樣源的;spring

  四、跨域資源共享(CORS):sql

             1)它是一份瀏覽器技術的規範,提供Web服務從不一樣域傳來沙盒腳本的方法,泳衣避開瀏覽器的同源策略,JSONP模式的現代版(相關JSONP的知識請本身查閱,本文再也不作解釋說明);小程序

     2)實現思路是使用自定義的HTTP頭部制定一個能夠互相承認的約定(例如瀏覽器給服務器傳送一個頭信息A,服務器給瀏覽器傳送一個頭信息B,就能夠不受同源策略的限制),從而決定請求與響應成功與否;後端

     3)CORS不關心安全問題,只解決資源共享的問題;安全性能夠從請求時效性,token驗證,IP驗證,來源驗證等方面考慮安全問題;

  五、CORS與JSONP的區別:

              1)JSONP只能實現GET請求,而CORS支持全部的請求;

      2)使用CORS,前端開發者可使用普通的XMLHttpRequest發起的請求和響應的數據,比JSONP有更好的錯誤處理;

      3)JSONP主要被版本比較老的瀏覽器支持,這些老版本的瀏覽器每每不支持CORS(IE6,IE7,Open mini),而絕大多數現代瀏覽器都已經支持了CORS;

  六、注:在跨域請求中,請求是能夠發送到服務器的,服務器也能夠響應數據,但服務器響應到瀏覽器的數據,瀏覽器拒絕解析(不太肯定是拒絕接受仍是拒絕解析?),致使的資源沒法共享;

解決方案

只要解決跨域資源共享便可,CROS解決方案主要有如下幾種:

  一、自定義CORSFilter(Intercepter):適用於設置單一的(所有)受權訪問,全部配置固定,操做簡單,不根據請求類型作不一樣的處理,粒度比較大;

    
 1 @Bean
 2 public FilterRegistrationBean corsFilter() {
 3     UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
 4     CorsConfiguration config = new CorsConfiguration();
 5     config.setAllowCredentials(true);    config.addAllowedOrigin("http://localhost:9000");
 6     config.addAllowedOrigin("null");
 7     config.addAllowedHeader("*");
 8     config.addAllowedMethod("*");
 9     source.registerCorsConfiguration("/**", config); // CORS 配置對全部接口都有效
10     FilterRegistrationBean bean = newFilterRegistrationBean(new CorsFilter(source));
11     bean.setOrder(0);
12     return bean;
13 }
View Code

  二、Nginx代理配置(配置在location中)

    
 1 #
 2 # Wide-open CORS config for nginx
 3 #
 4 location / {
 5      if ($request_method = 'OPTIONS') {
 6         add_header 'Access-Control-Allow-Origin' '*';
 7         add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
 8         #
 9         # Custom headers and headers various browsers *should* be OK with but aren't
10         #
11         add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
12         #
13         # Tell client that this pre-flight info is valid for 20 days
14         #
15         add_header 'Access-Control-Max-Age' 1728000;
16         add_header 'Content-Type' 'text/plain charset=UTF-8';
17         add_header 'Content-Length' 0;
18         return 204;
19      }
20      if ($request_method = 'POST') {
21         add_header 'Access-Control-Allow-Origin' '*';
22         add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
23         add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
24      }
25      if ($request_method = 'GET') {
26         add_header 'Access-Control-Allow-Origin' '*';
27         add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
28         add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
29      }
30 }
View Code(細粒度配置)
    
1 #js跨域支持
2                 #add_header 'Access-Control-Allow-Origin' '*';
3                 #add_header 'Access-Control-Allow-Credentials' 'true';
4                 #add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,X-Requested-With';
5                 #add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
View Code(粗粒度配置)

  三、使用spring框架提供的註解@CrossOrigin

    1)第一種狀況,對接口的粒度進行配置    

    
1 @CrossOrigin(origins = {"http://localhost:9000", "null"})
2 @RequestMapping(value = "/test", method = RequestMethod.GET)
3 public String greetings() {
4     return "{\"project\":\"just a test\"}";
5 }
View Code(接口)

    2)第二種狀況,對類的粒度進行配置  

    
1 @CrossOrigin(origins = {"http://localhost:9000", "null"})
2 @RestController
3 @SpringBootApplication
4 public class SpringBootCorsTestApplication {
5     // xxx
6 }
View Code

  四、全局配置

    
 1 @Configuration
 2 public class WebConfig extends WebMvcConfigurerAdapter {
 3 
 4     @Override
 5     public void addCorsMappings(CorsRegistry registry) {
 6         registry.addMapping("/**")
 7                 .allowedOrigins("http://localhost:9000", "null")
 8                 .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
 9                 .maxAge(3600)
10                 .allowCredentials(true);
11     }
12 }
View Code

  五、支持多域名配置的CORSFilter

    由於知道已經有能夠用的庫能夠解決,因此就沒重複造輪子了。其實由於懶,看看別人的源碼算了.在mvnrepository搜索cors-filter,目前也就兩個能夠用

    org.ebaysf.web 的 cors-filter,項目地址:https://github.com/ebay/cors-filter

            com.thetransactioncompany的 cors-filter,項目地址:http://software.dzhuvinov.com/cors-filter.htm

-------------------------------------------------------------------------------------------------

Ps:與前端的交互:

  非簡單請求的跨源請求,瀏覽器會在真實請求發出前,增長一次 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 相關的字段有:

                     Access-Control-Request-Method: 真實請求使用的 HTTP 方法;

                     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 

  他們的含義分別是:

  1. Access-Control-Allow-Methods: 真實請求容許的方法
  2. Access-Control-Allow-Headers: 服務器容許使用的字段
  3. Access-Control-Allow-Credentials: 是否容許用戶發送、處理 cookie
  4. Access-Control-Max-Age: 預檢請求的有效期,單位爲秒。有效期內,不會重複發送預檢請求

當預檢請求經過後,瀏覽器會發送真實請求到服務器。這就實現了跨源請求

-------------------------------------------------------------------------------------------------

總結:以上提供的幾種方案均可以解決跨域問題,視本身業務的實際狀況選擇方案;我的選擇的是最簡單的Nginx,不對代碼進行任何的修改,直接配置在代理中;我的以爲要達到比較細粒度的各類優化,推薦使用第五種方案,但同時會會增長學習成本,對時間比較充足的同窗能夠研究研究;

  雖然跨域調用的問題解決了,可是後來發現以上方法並不能解決cookies的跨域問題,致使在A域名下點擊登陸後直接跳轉到B域名下已登錄後的一個頁面,會失敗。主要問題是在A域名下獲取到的cookie不能共享到B域名下,致使跳轉B域名下已登陸的頁面會被斷定沒有coolie(沒有登陸),會致使直接跳轉到B域名下的登陸頁面進行從新登陸,即在A域名下的登陸失敗了;思考了幾種解決方案:①後端修改登陸接口的代碼邏輯,適配出現的這種狀況(建立新的cookie,或者由後端進行路由),②在A域名下點擊登陸時,直接跳轉到B域名下的一個和A域名頁面同樣的登陸頁面,不給用戶感知,但域名會發生變化,在B域名下的登陸頁面進行實際的登陸操做,就不會再產生cookie跨域的問題了,③直接使用nginx進行路由:

以上三種解決思路可供參考,我本身選用的第二種,若是後端和運維支持比較給力,推薦選用第一種或者第三種解決方案!!!!

參考Blog:一、http://www.javashuo.com/article/p-oebladsp-u.html

             二、http://www.cnblogs.com/sloong/p/cors.html

相關文章
相關標籤/搜索