前端頁面調用Spring boot接口發生的跨域問題


最近要重構一個基於spring boot的后端API服務,需要再本地測試。在本地測試時,運行在本地的前端頁面發送一個ajax請求訪問后端API,然后瀏覽器報錯blocked CORS policy。

Access to XMLHttpRequest at 'http://127.0.0.1:1234/api/' from origin 'http://dev.couchbase.cloud.qiyi.domain' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

最后的解決方法很簡單,只需要在相應的Controller上加一個注解@CrossOrigin就可以了,也可以將注解加到相應的方法上面。

 

為什么有些時候調用服務器的接口時沒有發生這個報錯?

原因:瀏覽器發送ajax請求必須是同源的,也就是瀏覽器訪問服務器接口需要考慮同源,而服務器與服務器之間訪問接口是不需要考慮同源的。

 

(一)同源策略(SOP策略)

同源策略(Same-Origin-Policy):URL由協議、域名、端口和路徑組成,如果兩個URL的協議、域名和端口相同,則表示他們同源。相反,只要協議,域名,端口有任何一個的不同,就被當作是跨域。

瀏覽器采用同源策略,禁止頁面加載或執行與自身來源不同的域的任何文檔或腳本,換句話說瀏覽器禁止的是來自不同源的"document"或腳本。在一個http請求中,http頭部Referer或Origin字段標識了當前域名,Host字段標識了此時請求的域名。因此我們在當前的js頁面,通過ajax請求第三方的數據,就會出現瀏覽器的跨域問題。

 

(二)跨域解決方法

1. 使用CORS策略

CORS策略:跨域資源共享(Corss Origin Resource Sharing)通過服務器增加一個特殊的Header[Access-Control-Allow-Origin]來告知客戶端跨域的限制,如果瀏覽器支持CORS的話,當判斷Origin通過的話,就會允許請求。使用這個Header返回被允許請求跨域請求的來源域,例如網站duelist.cn設置了下面的Header

Access-Control-Allow-Origin: http://smdcn.net

這樣設置之后,通過http://smdcn.net下的頁面對於duelist.cn進行ajax請求就會被允許,而其他網站對duelist.cn依舊會被阻攔,通過這種方式網站擁有者可以自己對此進行限制。當然,如果不想限制來源,可以通過來允許任何站點對該資源進行跨域請求

Access-Control-Allow-Origin: *

CORS規范中常見的頭信息:

常見的頭信息包括:

  Request Headers:

  Origin、Access-Control-Request-Method、Access-Control-Request-Headers

  Response Headers:

  1. 允許向該服務器提交請求的URI

  Access-Control-Allow-Origin: <origin> | *

  2. 瀏覽器允許訪問的服務器的頭信息的白名單

  Access-Control-Expose-Headers: ..., ...

  3. 請求有效期(單位:秒):

  Access-Control-Max-Age: <seconds>

  4. 允許的請求方法:

  Access-Control-Allow-Methods

  5. 實際的請求中,可以使用的自定義HTTP請求頭

  Access-Control-Allow-Headers

  6. 告知客戶端,當請求XHR的withCredientials屬性是true的時候,響應是否可以被得到。(從而使得下一次請求時,上一次的Cookies可以隨着請求發送)

  Access-Control-Allow-Credentials:

 

在傳統的Spring MVC中的使用方法:(網上較多資料都是這種方法)

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;

@Component
public class SimpleCORSFilter implements Filter {

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        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");
        chain.doFilter(req, res);
    }

    public void init(FilterConfig filterConfig) {}

    public void destroy() {}

}
<filter>
      <filter-name>cors</filter-name>
      <filter-class>com.app.filter.SimpleCORSFilter</filter-class>
    </filter>
    <filter-mapping>
      <filter-name>cors</filter-name>
      <url-pattern>/*</url-pattern>
    </filter-mapping>

 

在Spring boot中的使用方法就是添加注解@CrossOrigin

向@RequestMapping注解的Controller方法處添加一個@CrossOrigin注解:

@RestController
@RequestMapping("/account")
public class AccountController {

    @CrossOrigin
    @GetMapping("/{id}")
    public Account retrieve(@PathVariable Long id) {
        // ...
    }

    @DeleteMapping("/{id}")
    public void remove(@PathVariable Long id) {
        // ...
    }
}

為整個controller啟用@CrossOrigin:

@CrossOrigin(origins = "http://domain2.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {

    @GetMapping("/{id}")
    public Account retrieve(@PathVariable Long id) {
        // ...
    }

    @DeleteMapping("/{id}")
    public void remove(@PathVariable Long id) {
        // ...
    }
}

其中@CrossOrigin中的2個參數:

origins  : 允許可訪問的域列表

maxAge:准備響應前的緩存持續的最大時間(以秒為單位)。

 

 

2. 使用jsonp

jsonp解決跨域問題的原理是,瀏覽器的script標簽是不受同源策略限制的,我們可以在script標簽中訪問任何域名下的資源文件。利用這一特性,用script標簽從服務器中請求數據,同時服務器返回一個帶有方法和數據的js代碼,請求完成,調用本地的js方法,來完成數據的處理。

不推薦使用jsonp,首先jsonp是一種非官方的方法,而且這種方法只支持GET方法,不如POST方法安全;而且一般前后端分離最通用的方法是返回統一的json格式。

 

3. 服務器代理

這種方式運用的就是服務器的反向代理技術,控制客戶端和服務器的訪問都從代理服務器經過,比如用nginx作為服務器代理,在nginx上配置客戶端和第三方服務的反向代理,這樣就可保證客戶端、第三方是同源的了,同一個源,都來自代理服務器。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM