跨域問題

備注:有時候項(xiàng)目有點(diǎn)忙,都忘記思考,一次面試中提出的跨域的問題,才恍然大悟,做了一下小的總結(jié),望指正!

過濾攔截請求!?。?/p>

由于工程合作開發(fā)的需要,后臺的應(yīng)用要能支持跨域訪問,但是在這個跨域訪問“時好時壞”,我們這幫屌絲所知道的就是加上兩個jar包,然后聲明一下Filter,感覺很簡單的有沒有?。「杏X自己很牛X有沒有??!全是幻覺!!要不然怎么會時好時壞!!為了深入了解這個問題,決定寫這篇文章總結(jié)一下。

要知道跨域請求就要先了解同源策略,那么什么是同源?什么是不同源?簡單來說就是,如果兩個資源,包括HTML頁面、JavaScript腳本、css樣式,對應(yīng)的協(xié)議、域名和端口完全相同,那么這兩個資源就是同源的,Same-origin policy解釋得很清楚。那么同源策略的意思就是一個源中的資源訪問另外一個源中的資源,在在這一點(diǎn)上JavaScript的跨站資源訪問表現(xiàn)的更加明顯。在HTML5之前Ajax是不允許發(fā)起跨站請求的,如果有需求的話,可以使用JSONP等方法,但是缺點(diǎn)就是:

只支持Get不支持Post;

本質(zhì)上是腳本注入的方式,存在安全隱患;

還有JSONP的優(yōu)缺點(diǎn),但是自從HTML5出現(xiàn)之后,提出了CORS(跨站資源共享)這種方式,極大地方便了日常的開發(fā)。如果要理解CORS的工作原理,首先要知道跨域訪問是怎么被禁止的,之前本屌絲一直以為是前臺的跨域訪問請求不能發(fā)出去,是實(shí)現(xiàn)同源策略的瀏覽器攔截了該請求,但是后來才知道瀏覽器并沒有攔截請求,而是攔截了服務(wù)器端返回的響應(yīng)。

所以如果要支持跨域訪問,需要瀏覽器和后臺服務(wù)器程序同時支持,如果這兩個條件不能同時滿足,則還是不能支持跨域訪問。

用于CORS中的Http的首部有如下幾個:

響應(yīng)頭

Access-Control-Allow-Origin: 允許跨域訪問的域,可以是一個域的列表,也可以是通配符”*”;

Access-Control-Allow-Methods: 允許使用的請求方法,以逗號隔開;

Access-Control-Allow-Headers: 允許自定義的頭部,以逗號隔開,大小寫不敏感;

Access-Control-Expose-Headers: 允許腳本訪問的返回頭,請求成功后,腳本可以在XMLHttpRequest中訪問這些頭的信息

Access-Control-Allow-Credentials: 是否允許請求帶有驗(yàn)證信息,XMLHttpRequest請求的withCredentials標(biāo)志設(shè)置為true時,認(rèn)證通過,瀏覽器才將數(shù)據(jù)給腳本程序。

Access-Control-Max-Age: 緩存此次請求的秒數(shù)。在這個時間范圍內(nèi),所有同類型的請求都將不再發(fā)送預(yù)檢請求而是直接使用此次返回的頭作為判斷依據(jù),非常有用,大幅優(yōu)化請求次數(shù);

請求頭

Origin: 普通的HTTP請求也會帶有,在CORS中專門作為Origin信息供后端比對,表明來源域,要與響應(yīng)頭中的Access-Control-Allow-Origin相匹配才能進(jìn)行跨域訪問;

Access-Control-Request-Method: 將要進(jìn)行跨域訪問的請求方法,要與響應(yīng)頭中的Access-Control-Allow-Methods相匹配才能進(jìn)行跨域訪問;

Access-Control-Request-Headers: 自定義的頭部,所有用setRequestHeader方法設(shè)置的頭部都將會以逗號隔開的形式包含在這個頭中,要與響應(yīng)頭中的Access-Control-Allow-Headers相匹配才能進(jìn)行跨域訪問

從支持跨域訪問的范圍說,可以有整個服務(wù)器、單個應(yīng)用程序、單個接口。

java服務(wù)器端解決跨域問題:現(xiàn)在很多開發(fā)的API都支持ajax直接請求,這樣就會導(dǎo)致跨域的問題,解決跨域的問題一方面可以從前端,另一方面就是服務(wù)器端。

既然是搞服務(wù)器端,做對外的API服務(wù),當(dāng)然是做到越簡單越好,前端只需要傻傻的使用就好。

目前我接觸來的情況是有2種實(shí)現(xiàn)方式,下面直接代碼,你們根據(jù)自己項(xiàng)目情況,選擇或者修改其中的代碼,所有代碼都是項(xiàng)目實(shí)戰(zhàn)中運(yùn)行的。

第一種情況,比較簡單,讓所有的controller類繼承自定義的BaseController類,改類中將對返回的頭部做些特殊處理。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

public abstract class BaseController {

? /**

? ? * description:send the ajax response back to the client side

? ? * @param responseObj

? ? * @param response

? ? */

? ? protected void writeAjaxJSONResponse(Object responseObj, HttpServletResponse response) {

? ? ? ? response.setCharacterEncoding("UTF-8");

? ? ? ? response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1

? ? ? ? response.setHeader("Pragma", "no-cache"); // HTTP 1.0

? ? ? ? /**

? ? ? ? * for ajax-cross-domain request TODO get the ip address from

? ? ? ? * configration(ajax-cross-domain.properties)

? ? ? ? */

? ? ? ? response.setHeader("Access-Control-Allow-Origin", "*");

? ? ? ? response.setDateHeader("Expires", 0); // Proxies.

? ? ? ? PrintWriter writer = getWriter(response);

? ? ? ? writeAjaxJSONResponse(responseObj, writer);

? ? }

? /**

? ? *

? ? * @param response

? ? * @return

? ? */

? ? protected PrintWriter getWriter(HttpServletResponse response) {

? ? ? ? if(null == response){

? ? ? ? ? ? return null;

? ? ? ? }

? ? ? ? PrintWriter writer = null;

? ? ? ? try {

? ? ? ? ? ? writer = response.getWriter();

? ? ? ? } catch (IOException e) {

? ? ? ? ? ? logger.error("unknow exception", e);

? ? ? ? }

? ? ? ? return writer;

? ? }

? ? /**

? ? * description:send the ajax response back to the client side.

? ? *

? ? * @param responseObj

? ? * @param writer

? ? * @param writer

? ? */

? ? protected void writeAjaxJSONResponse(Object responseObj, PrintWriter writer) {

? ? ? ? if (writer == null || responseObj == null) {

? ? ? ? ? ? return;

? ? ? ? }

? ? ? ? try {? ? ? ? writer.write(JSON.toJSONString(responseObj,SerializerFeature.DisableCircularReferenceDetect));

? ? ? ? } finally {

? ? ? ? ? ? writer.flush();

? ? ? ? ? ? writer.close();

? ? ? ? }

? ? }

}

接下來就是我們自己業(yè)務(wù)的controller了,其中主要是要調(diào)用 writeAjaxJSONResponse(result, response);這個方法

?

1

2

3

4

5

6

7

8

9

10

11

@Controller

@RequestMapping(value = "/account")

public class AccountController extends BaseController {

@RequestMapping(value = "/add", method = RequestMethod.POST)

? ? public void addAccount(HttpSession session,HttpServletRequest request,HttpServletResponse response){

? ? ? ? ViewerResult result = new ViewerResult();

? ? ? ? //實(shí)現(xiàn)自己業(yè)務(wù)邏輯代碼

? ? ? ? writeAjaxJSONResponse(result, response);

? ? }

}

?

1

2

3

4

好了,這種簡單的方式就實(shí)現(xiàn)了。

接下來介紹第二種方式,filter。我們在寫springMVC的時候,更喜歡的方式是通過@ResponseBody給返回對象進(jìn)行封裝直接返回給前端,這樣簡單而且容易。

如果使用@ResponseBody就不能使用第一種方法了,所有就使用filter給所有的請求都封裝一下跨域,接下來直接實(shí)現(xiàn)代碼:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

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;

public class HeadersCORSFilter implements Filter {

? ? @Override

? ? public void init(FilterConfig filterConfig) throws ServletException {

? ? ? ? // TODO Auto-generated method stub

? ? }

? ? @Override

? ? public void doFilter(ServletRequest request, ServletResponse servletResponse,

? ? ? ? ? ? FilterChain chain) throws IOException, ServletException {

? ? ? ? HttpServletResponse response = (HttpServletResponse) servletResponse;

? ? ? ? ? ? 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");

? ? ? ? ? ? chain.doFilter(request, servletResponse);

? ? }

? ? @Override

? ? public void destroy() {

? ? ? ? // TODO Auto-generated method stub

? ? }

}

好了,filter實(shí)現(xiàn)了,然后就是要在web.xml里面把這個filter運(yùn)用起來了。

打開項(xiàng)目的web.xml,填寫下面的幾行代碼:

?

1

2

3

4

5

6

7

8

<filter>

? <filter-name>cors</filter-name>

? <filter-class>xxx.xxxx.xxxxx.xxxx.HeadersCORSFilter</filter-class><!--你過濾器的包 -->

</filter>

<filter-mapping>

? <filter-name>cors</filter-name>

? <url-pattern>/open/*</url-pattern><!-- 你開放的接口前綴? -->

</filter-mapping>

?

1

2

好了,通過上面的2種方式,可以解決百分之80的跨域問題,也許還有更好的解決方案,可以提出來大家一起學(xué)習(xí)學(xué)習(xí)。

最好的方案是最符合當(dāng)前需求且易于擴(kuò)展的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容