Java跨域問題以及如何使用Cors解決前后端 分離部署項目所遇到的跨域問題
什么是跨域
跨域,指的是瀏覽器不能執行其他網站的腳本。它是由瀏覽器的同源策略造成的,是瀏覽器對javascript施加的安全限制。
什么情況下不是跨域?
即:滿足域名、協議、端口均相同的即不是跨域。
場景
現在我們進行前后端分離的部署,將前端的代碼與后端的代碼放到不同的服務器上,此時前端要使用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://baidu.com
代碼實現
首先經過對上面的閱讀我們對跨域以及cors進行了初步的了解。現在我們來思考一個問題。跨域應該前端進行配置還是后端進行配置?答案當然是后端。試想一下,如果這個問題是由前端解決而后臺不需要任何配置,那么豈不是你隨便寫一個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)
/**
- @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)
//證明是一個配置文件(使用@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;
}
}
寫這篇文章的目的主要是因為現在前后端分離越來越多,跨域問題碰到的概率越來越高,所以寫出這篇文章,希望可以幫助到有需要的同學。
本文參考csdn中beyondLi71同學的文章