Spring通過CROS協議解決跨域問題

duoduobear 8年前發布 | 76K 次閱讀 Spring Ajax JEE框架

現在接手學校網絡中心的一個項目,根據團隊成員的實際情況以及開發需要,老師希望做到前后端完全分離。后臺使用java提供restful API 作為核心,前臺無論PC或者移動端可以共用一個核心。前期解決了哦oauth2,作為授權機制等問題,本以為大業將成。(最近打算詳細介紹一下機遇Spring sercurity 實現oauth2的解決方案)結果又出現了一個跨域問題,讓我們踩了一個大坑,記錄在此,以絕后患。

錯誤信息如下:

Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. The response had HTTP status code 403.

什么是跨域

簡單的說即為瀏覽器限制訪問A站點下的js代碼對B站點下的url進行ajax請求。比如說,前端域名是www.abc.com,那么在當前環境中運行的js代碼,出于安全考慮,訪問www.xyz.com域名下的資源,是受到限制的。現代瀏覽器默認都會基于安全原因而阻止跨域的ajax請求,這是現代瀏覽器中必備的功能,但是往往給開發帶來不便。特別是對我這樣后臺開發人員來講,這個事情簡直神奇。

但跨域的需求卻一直都在,為了跨域,勤勞勇敢的程序猿們想出了許許多多的方法,例如,jsonP、代理文件等等。但這些做法增加了許多不必要的維護成本,而且應用場景也有許多限制,例如jsonP并非XHR,所以jsonP只能使用GET傳遞參數。更詳細的資料可以看這里 Web應用跨域訪問解決方案匯總

CORS協議

如今的JS大有一統天下的趨勢,瀏覽器已經成了大多應用最好的安身之所。哪怕在移動端也有各種Hybird方案,在本地文件系統的Web頁面,也有需要獲取外部數據的需求,而這些需求也必然是跨域的。在尋找跨域解決方案時,發現了最優雅解決方案就是HTML5來帶了的“Cross-Origin Resource Sharing”的新特性,來賦予開發者權力決定資源是否允許被跨域訪問。

CORS是一個W3C標準,全稱是”跨域資源共享”(Cross-origin resource sharing)。

它允許瀏覽器向跨源服務器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。

為什么說它優雅呢?

整個CORS通信過程,都是瀏覽器自動完成,不需要用戶參與。對于開發者來說,CORS通信與同源的AJAX通信沒有差別,代碼完全一樣。瀏覽器一旦發現AJAX請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感覺。

因此,實現CORS通信的關鍵是服務器。 只要服務器實現了CORS接口 ,就可以跨源通信。

解決這個問題的關鍵就落在了我這個負責后臺的程序猿身上。

看看文檔也不是什么難事嘛,就是需要在http頭中設置Access-Control-Allow-Origin來決定需要允許哪些站點來訪問。關于CROS協議更詳細內容參考 跨域資源共享 CORS 詳解

CROS常見header

CORS具有以下常見的header

Access-Control-Allow-Origin: http://kbiao.me  

Access-Control-Max-Age: 3628800

Access-Control-Allow-Methods: GET,PUT, DELETE

Access-Control-Allow-Headers: content-type

“Access-Control-Allow-Origin”表明它允許”http://kbiao.me “發起跨域請求

“Access-Control-Max-Age”表明在3628800秒內,不需要再發送 預檢驗 請求,可以緩存該結果(上面的資料上我們知道CROS協議中,一個AJAX請求被分成了第一步的 OPTION 預檢測請求和正式請求)

“Access-Control-Allow-Methods”表明它允許GET、PUT、DELETE的外域請求

“Access-Control-Allow-Headers”表明它允許跨域請求包含content-type頭

當然在處理這個問題的時候前端也有不少坑,特別是Chrome瀏覽器不允許localhost的跨域請求,詳細方案見我項目中負責前端解決方案的小伙伴。 假裝有鏈接

常規解決方案

知道了問題的原因,也知道了配套的解決辦法,現在就讓我們來實現解決。思路很簡單,當前端要請求跨域資源時候,我們給它加上響應的響應頭即可。很顯然我們自己定義一個過濾器是最簡單不過了。

/**
 * Created by kangb on 2016/5/10.
 */
@Component
public class myCORSFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String origin = (String) servletRequest.getRemoteHost()+":"+servletRequest.getRemotePort();
        response.setHeader("Access-Control-Allow-Origin", "*");
        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,Authorization");
        response.setHeader("Access-Control-Allow-Credentials","true");
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}

@Component 是Spring的注解,關鍵部分在doFilter中,添加了我們需要的頭, option 是預檢測需要所以需要允許, Authorization 是做了oauth2登錄響應所必須的, Access-Control-Allow-Credentials 表示允許cookies。都是根據自己項目的實際需要配置。

再配置Web.xml使得過濾器生效

<filter>
  <filter-name>cors</filter-name>
  <filter-class>·CLASS_PATH·.myeCORSFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>cors</filter-name>
  <url-pattern>/api/*</url-pattern>
</filter-mapping>

接下來前端就可以像往常一樣使用AJAX請求獲得資源了,完全不需要做出什么改變。

SPRING 4中更優雅的辦法

SpringMVC4提供了非常方便的實現跨域的方法。在requestMapping中使用注解。

@CrossOrigin(origins = “http://kbiao.me”)

全局實現 .定義類繼承WebMvcConfigurerAdapter,設置跨域相關的配置

public class CorsConfigurerAdapter extends WebMvcConfigurerAdapter{

  @Override
  public void addCorsMappings(CorsRegistry registry) {

    registry.addMapping("/api/*").allowedOrigins("*");
  }
}

將該類注入到容器中:

<bean class="com.tmall.wireless.angel.web.config.CorsConfigurerAdapter"></bean>

更詳細的內容參考Spring 官方的hello world案例 Enabling Cross Origin Requests for a RESTful Web Service

http://spring.io/guides/gs/rest-service-cors/或者使用git下載示例源碼

https://github.com/spring-guides/gs-rest-service-cors.git

 

來自: http://blog.kbiao.me/2016/05/14/Spring通過CROS協議解決跨域問題/

 

 本文由用戶 duoduobear 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!