JAVA WEB之跨域問題以及解決方案(上)

目錄

一、產生跨域的原因

二、錯誤描述

三、HTTP訪問控制(CORS)

四、解決方案

      1、@CrossOrigin 註解
      2、設置頭部信息
      3、配置攔截器
      4、web.xml配置
      5、HttpClient 轉發請求
      6、spring-context.xml配置

五、帶token的跨域問題解決方案


JAVA WEB之跨域問題以及解決方案(下)

一、產生跨域的原因

我們在項目開發中,經常會使用前後端分離的技術,把前端代碼和後端代碼分別部署到不同的服務器上,這樣在數據交互的時候,由於瀏覽器的同源策略,就會產生跨域問題。

同源策略,它是由Netscape提出的一個著名的安全策略。
現在所有支持JavaScript 的瀏覽器都會使用這個策略。
所謂同源是指,域名,協議,端口相同。
如果非同源,那麼在請求數據時,瀏覽器會在控制檯中報一個異常,提示拒絕訪問。

同源策略是瀏覽器的行爲,是爲了保護本地數據不被JavaScript代碼獲取回來的數據污染,因此攔截的是客戶端發出的請求回來的數據接收,即請求發送了,服務器響應了,但是無法被瀏覽器接收。

http://www.a.com:8080/b.js

二、錯誤描述

Failed to load https://example.com/: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘https://anfo.pl’ is therefore not allowed access. If an opaque response serves your needs, set the request’smode to ‘no-cors’ to fetch the resource with CORS disabled.
在這裏插入圖片描述
在這裏插入圖片描述

三、HTTP訪問控制(CORS)

跨域資源共享(CORS) 是一種機制,它使用額外的 HTTP 頭來告訴瀏覽器 讓運行在一個指定域名上的Web應用被准許訪問來自不同源服務器上的指定的資源。

  • 簡單請求:不會觸發 CORS 預檢請求(GET、HEAD、POST)
  • 預檢請求:必須首先使用 OPTIONS 方法發起一個預檢請求到服務器,以獲知服務器是否允許該實際請求。預檢請求“的使用,可以避免跨域請求對服務器的用戶數據產生未預期的影響。(PUT、DELETE、CONNECT、OPTIONS、TRACE PATCH)
  • HTTP 響應首部字段:Access-Control-Allow-Origin、Access-Control-Expose-Headers、Access-Control-Max-Age、Access-Control-Allow-Credentials、Access-Control-Allow-Methods、Access-Control-Allow-Headers(在下文使用時,再做詳細介紹)
  • HTTP 請求首部字段:Origin、Access-Control-Request-Method、Access-Control-Request-Headers(在下文使用時,再做詳細介紹)

更多CORS的細節…

四、解決方案

1、@CrossOrigin 註解
  • 適用於jdk 1.8及以上
  • @CrossOrigin:允許所有ip跨域訪問
  • @CrossOrigin(origins=“xxx.xxx.xxx.xxx”):只允許指定ip跨域

直接在Controller類或者方法上添加

@CrossOrigin
@Controller
public class BookController {
	//do something...
}

2、設置頭部信息

首先要了解一下以下幾個名詞:

1、 CacheController(http協議的緩存控制),常見的值有:

  • no-cache:告訴瀏覽器、緩存服務器,不管本地副本是否過期,使用資源副本前,一定要到源服務器進行副本有效性校驗。
  • must-revalidate:告訴瀏覽器、緩存服務器,本地副本過期前,可以使用本地副本;本地副本一旦過期,必須去源服務器進行有效性校驗。
  • no-store:用於防止重要的信息被無意的發佈,在請求消息中發送將使得請求和響應消息都不使用緩存。
  • 注意:僅適用於http 1.1版本

2、Pragma:

  • no-cache:指示請求或響應消息不能緩存。即,資源不能從cache中獲取,而必須回源獲取。
  • 兼容http 1.0 和 http 1.1版本

3、Expires:瀏覽器會在指定過期時間內使用本地緩存,指明應該在什麼時候認爲文檔已經過期,從而不再緩存它,時間爲格林威治時間GMT。

4、Access-Control-Allow-Origin:指定允許訪問該資源的外域 URI,它的值可爲:

  • 一個完整的域名(例如,http://www.baidu.com)
  • " * " :允許任意域名

接下來是實現代碼:

1、定義Controller類的父類,對頭部信息進行設置

package test;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;

public class SetHeaderController {

    protected void writeJsonResponse(Object responseObj, HttpServletResponse response) {
        response.setCharacterEncoding("UTF-8");

        //禁用緩存,確保網頁信息是最新數據
        // HTTP 1.1
        response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
        // HTTP 1.0
        //response.setHeader("Pragma", "no-cache");

        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setDateHeader("Expires", 0);
        PrintWriter writer = getWriter(response);
        writeJsonResponse(responseObj, writer);
    }

    protected PrintWriter getWriter(HttpServletResponse response) {
        if (null == response) {
            return null;
        }

        PrintWriter writer = null;
        try {
            writer = response.getWriter();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return writer;
    }


    protected void writeJsonResponse(Object responseObj, PrintWriter writer) {
        if (writer == null || responseObj == null) {
            return;
        }
        try {
            writer.write(JSON.toJSONString(responseObj, SerializerFeature.DisableCircularReferenceDetect));
        } finally {
            writer.flush();
            writer.close();
        }
    }
}

實現功能的Controller調用代碼:
Message類是自己寫的,用於封裝返回給前端的信息

package test;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import po.Message;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@Controller
public class BookController extends SetHeaderController {

    @RequestMapping("/addBook.do")
    public void addBook(HttpSession httpSession, HttpServletRequest httpServletRequest, HttpServletResponse
                        httpServletResponse){
        Message message = new Message();
        //do something...
        writeJsonResponse(message, httpServletResponse);
    }
}

3、配置攔截器

首先介紹攔截器使用到的幾個名詞:

1、Access-Control-Allow-Methods:首部字段用於預檢請求的響應。其指明瞭實際請求所允許使用的 HTTP 方法。

2、Access-Control-Max-Age:指定了preflight請求的結果能夠被緩存多久。(以秒爲單位)

3、Access-Control-Allow-Headers: 用於預檢請求的響應。其指明瞭實際請求中允許攜帶的首部字段。

4、Access-Control-Allow-Credentials :指定了當瀏覽器的credentials設置爲true時是否允許瀏覽器讀取response的內容。

  • 當用在對preflight預檢測請求的響應中時,它指定了實際的請求是否可以使用credentials。
  • 請注意:簡單 GET 請求不會被預檢;如果對此類請求的響應中不包含該字段,這個響應將被忽略掉,並且瀏覽器也不會將相應內容返回給網頁。

Access-Control-Allow-Origin已經在前面解釋過,這裏就不再贅述。

攔截器代碼:

package test;

import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class CORSFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        HttpServletResponse resp = (HttpServletResponse) response;

        resp.setContentType("text/html;charset=UTF-8");
        
        resp.setHeader("Pragma","No-cache");
        resp.setHeader("Cache-Control","no-cache");
        resp.setHeader("Access-Control-Allow-Origin", "*");
        resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, HEAD, DELETE, PUT");
        resp.setHeader("Access-Control-Max-Age", "3600");
        resp.setHeader("Access-Control-Allow-Headers",
                "X-Requested-With, Content-Type, Authorization, Accept, Origin, User-Agent, Content-Range, Content-Disposition, Content-Description");

        resp.setDateHeader("Expires", -10);
        chain.doFilter(request, resp);
    }

    @Override
    public void init(FilterConfig filterConfig) {}

    @Override
    public void destroy() {}
}

在web.xml中聲明該過濾器:

<filter>
	<filter-name>cors</filter-name>
	<filter-class>test.CORSFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>cors</filter-name>
	<!-- 開放的接口前綴 -->
	<url-pattern>/*</url-pattern>
</filter-mapping>
  • 注意:放在其他過濾器的前面

JAVA WEB之跨域問題以及解決方案(下)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章