1. 程式人生 > >java 淺析跨域問題以及如何使用Cors解決前後端分離部署專案所遇到的跨域問題

java 淺析跨域問題以及如何使用Cors解決前後端分離部署專案所遇到的跨域問題

隨著時間的推移,前後端分離的開發形式越來越流行,使用的公司也越來越多。但是這種開發形式也會帶來一個問題。那就是跨域問題。

什麼是跨域

跨域,指的是瀏覽器不能執行其他網站的指令碼。它是由瀏覽器的同源策略造成的,是瀏覽器對javascript施加的安全限制。
什麼情況下不是跨域?
即:滿足域名、協議、埠均相同的即不是跨域。

例如
http://www.beyondLi.com/index.html 呼叫 http://www.beyondLi.com/server (非跨域)

http://www.beyondLione.com/index.html 呼叫

http://www.beyondLitwo.com/server(主域名不同:one/two,跨域)

http://www.beyondLi.com:8080/index.html 呼叫 http://www.beyondLi.com:8081/server (埠不同:8080/8081,跨域)

http://www.beyondLi.com/index.html 呼叫 https://www.beyondLi.com/server (協議不同:http/https,跨域)

場景

現在我們進行前後端分離的部署,將前端的程式碼與後端的程式碼放到不同的伺服器上,此時前端要使用ajax請求來呼叫後臺的介面獲取到對應的資料。

解決方案

一般解決跨域問題的方案有兩種,jsonp以及cors。因為jsonp需要前後臺做配合統一標示個人感覺比較麻煩,所以本人選擇使用cors方案來解決此問題。

cors介紹

Cross-Origin Resource Sharing (CORS) 是W3c工作草案,它定義了在跨域訪問資源時瀏覽器和伺服器之間如何通訊。CORS背後的基本思想是使用自定義的HTTP頭部允許瀏覽器和伺服器相互瞭解對方,從而決定請求或響應成功與否。

cors與jsonp對比

CORS與JSONP相比,更為先進、方便和可靠。
1、 JSONP只能實現GET請求,而CORS支援所有型別的HTTP請求。
2、 使用CORS,開發者可以使用普通的XMLHttpRequest發起請求和獲得資料,比起JSONP有更好的錯誤處理。
3、 JSONP主要被老的瀏覽器支援,它們往往不支援CORS,而絕大多數現代瀏覽器都已經支援了CORS。
對一個簡單的請求,沒有自定義頭部,要麼使用GET,要麼使用POST,它的主體是text/plain,請求用一個名叫Orgin的額外的頭部發送。Origin頭部包含請求頁面的頭部(協議,域名,埠),這樣伺服器可以很容易的決定它是否應該提供響應。
伺服器端對於CORS的支援,主要就是通過設定Access-Control-Allow-Origin來進行的。
Header set Access-Control-Allow-Origin *
為了防止XSS攻擊我們的伺服器, 我們可以限制域,比如
Access-Control-Allow-Origin:

http://beyondLi.com

程式碼實現

首先經過對上面的閱讀我們對跨域以及cors進行了初步的瞭解。現在我們來思考一個問題。跨域應該前端進行配置還是後端進行配置?答案當然是後端。試想一下,如果這個問題是由前端解決而後臺不需要任何配置,那麼豈不是你隨便寫一個ajax就能訪問各種網站的各種介面了嗎。這就有點恐怖了。所以理所應當的應該我們後臺開發來配置這個是否允許跨域請求的開關,並且能做一定的限制和篩選。

前端程式碼

並沒有什麼特別,不做過多介紹。只是一個簡單的ajax訪問後端介面的請求。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>hello world</title>
    <script type="text/javascript" src="js/vue.js"></script>
    <script type="text/javascript" src="js/jquery-3.1.1/jquery-3.1.1.js"></script>
</head>
<body>
    <div id="app">
      <p>hello</p>
    </div>
</body>

<script>
$(function(){
        alert("ajax go");
        $.ajax({
            url:'http://192.168.14.131:8080/student/getInfo',
            type:'post',
            dataType:'text',
            success:function(data){
                alert(data);
            }
        });
    }
)
</script>

</html>

如果我們不做任何跨域處理訪問的話,會報如下錯誤,大概意思也就是說我們沒有配置Access-Control-Allow-Origin這個頭。
這裡寫圖片描述

後臺程式碼

我們的核心程式碼來了,其實很簡單,只要將報錯的屬性設定到response頭部,即可解決此問題,所以我們只要提供一個過濾器或者一個攔截全部的AOP即可解決此問題。程式碼如下(本人使用的springboot進行開發,只要瞭解問題的本質,將下面的方法有選擇性的使用即可。)

方法一(使用Filter)
/**
 * Created by beyondLi on 2017/7/29.
 * @desc 解決跨域
 */

@Component
public class CrossFilter 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", "*");
        response.setHeader("Access-Control-Max-Age", "1728000");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
//        response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
        chain.doFilter(req, res);
    }
    public void init(FilterConfig filterConfig) {}
    public void destroy() {}
}
方法二(使用AOP)
/**
 * Created by beyondLi on 2017/7/31.
 */
//證明是一個配置檔案(使用@Component也可以,因為點入後會發現@Configuration還是使用了@Component)
@Configuration
//證明是一個切面
@Aspect
public class ControllerAOP {
    //環繞aop
    //execution表示式 此表示式表示掃描controller下所有類的所有方法都執行此aop
    @Around("execution (* com.beyondli.controller..*.*(..))")
    public Object testAop(ProceedingJoinPoint pro) throws Throwable {
        //獲取response
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        //核心設定
        response.setHeader("Access-Control-Allow-Origin", "*");

        //執行呼叫的方法
        Object proceed = pro.proceed();
        return proceed;
    }

}

此時我們再進行訪問,效果如下,跨域問題完美解決。
這裡寫圖片描述

寫這篇文章的目的主要是因為現在前後端分離越來越多,跨域問題碰到的概率越來越高,所以寫出這篇文章,希望可以幫助到有需要的同學。