跨域講解學習二(服務端Filter解決跨域方法)


問題一、瀏覽器是先執行請求還是先判斷跨域?

  瀏覽器請求-->判斷響應中是否有允許跨域-->發現不允許跨域,阻止跨域

  說明:當執行跨域請求時,瀏覽器會提示當前接口不被允許,這說明瀏覽器已發出了當前請求,但是它的的響應內容被攔截;如果在Response header中的Access-Control-Allow-Origin設置的允許訪問源不包含當前源,則拒絕數據返回給當前源。

問題二、判斷當前請求是否是跨域請求?

  通過查看當前請求的Request Headers 中是否存在Origin屬性,當前屬性存儲的是當前域的信息

問題三、什么是簡單請求和非簡單請求?

  說明:瀏覽器在發送跨域請求時會先判斷當前請求是不是簡單請求,如果是簡單請求瀏覽器則會先執行請求,再判斷是否支持跨域;如果是非簡單請求它會先發送一個預檢命令(即OPTIONS請求),檢查通過后再把當前請求發出去。

  (1)簡單請求:方法為GET、HEAD、POST的請求,並且請求頭(header)里面沒有自定義頭;Content-Type為text/plain、multipart/form-data、application/x-www-form-urlencoded。

  (2)非簡單請求:方法為PUT、DELETE的請求,發送JSON格式的ajax請求、帶自定義請求頭的ajax請求。

  例如:發送JSON格式數據的ajax請求

 

$.ajax({
  type : "post",
  url: url,
  contentType :
"application/json;charset=utf-8",
  data: JSON.stringify({name:
"小明"}),
  success:
function(json){
    var result
= json;
  }
});

問題四:Access-Control-Allow-Headers是什么?有什么作用?

  響應頭部 Access-Control-Allow-Headers 用於 preflight request (預檢請求)中,列出了將會在正式請求的 Access-Control-Expose-Headers 字段中出現的首部信息。簡單首部,如 simple headers、Accept、Accept-Language、Content-Language、Content-Type (只限於解析后的值為 application/x-www-form-urlencoded、multipart/form-data 或 text/plain 三種MIME類型(不包括參數)),它們始終是被支持的,不需要在這個首部特意列出。

問題五:Access-Control-Max-Age是什么?
  瀏覽器的同源策略,就是出於安全考慮,瀏覽器會限制從腳本發起的跨域HTTP請求(比如異步請求GET, POST, PUT, DELETE, OPTIONS等等),所以瀏覽器會向所請求的服務器發起兩次請求,第一次是瀏覽器使用OPTIONS方法發起一個預檢請求,第二次才是真正的異步請求,第一次的預檢請求獲知服務器是否允許該跨域請求:如果允許,才發起第二次真實的請求;如果不允許,則攔截第二次請求。Access-Control-Max-Age用來指定本次預檢請求的有效期,單位為秒,,在此期間不用發出另一條預檢請求。

  例如:res.addHeader("Access-Control-Max-Age", "3600"),表示隔60分鍾才發起預檢請求

問題六:Access-Control-Allow-Origin:*是否滿足所有跨域場景?

  帶Cookie值的跨域請求,此時Access-Control-Allow-Origin:*無法滿足支持跨域請求。

 

$.ajax({
  type : "get",
  url: url,
  xhrFields:{
    withCredentials:true
  },
  success: function(json){
    var result = json;
  }
});

 

  說明:

  (1)當請求帶有Cookie信息時,Access-Control-Allow-Origin的值必須要跟請求域的信息完全相同,不能使用通配符"*";所以后台設置可以通過直接獲取請求信息中Access-Control-Allow-Origin值作為響應信息中Access-Control-Allow-Origin的值

HttpServletResponse res = (HttpServletResponse) response;
HttpServletRequest req = (HttpServletRequest) request;
String origin = req.getHeader("Origin");        
if (!org.springframework.util.StringUtils.isEmpty(origin)) {
  //帶cookie的時候,origin必須是全匹配,不能使用*
  res.addHeader("Access-Control-Allow-Origin", origin);            
}

  (2)當請求帶有Cookie信息的跨域請求時,Access-Control-Allow-Credentials的值設置為true

// enable cookie
res.addHeader("Access-Control-Allow-Credentials", "true");

 

  注意:

  (1)、帶Cookie的跨域時,Access-Control-Allow-Origin不能寫星號("*"),必須要寫具體的域名

  (2)、發送的Cookie必須是被調用方域名的Cookie,而不是調用方域名的Cookie

問題七、帶自定請求頭的跨域請求如何設置?

  JQuery的ajax請求如何設置自定義請求頭方法:

$.ajax({
  type : "get",
  url: url,
  headers:{
    "sessionId" : "123456"
  },
  beforeSend: function(xhr){
    xhr.setRequestHeader("token","654321")
  },
  success: function(json){
    var result = json;
  }
});

 服務端可設置支持所有自定義請求頭

String headers = req.getHeader("Access-Control-Request-Headers");
// 支持所有自定義頭
if (!org.springframework.util.StringUtils.isEmpty(headers)) {   res.addHeader("Access-Control-Allow-Headers", headers); }

 

在被調用方解決跨域(支持跨域)通過自定義Filter來實現:

(1)在SpringBoot工程創建自定攔截器CrosFilter,如圖:

package *;

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.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.tomcat.util.buf.StringUtils;

public class CrosFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // TODO Auto-generated method stub
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // TODO Auto-generated method stub

        HttpServletResponse res = (HttpServletResponse) response;
        HttpServletRequest req = (HttpServletRequest) request;
        String origin = req.getHeader("Origin");
        if (!org.springframework.util.StringUtils.isEmpty(origin)) {
            //帶cookie的時候,origin必須是全匹配,不能使用*
            res.addHeader("Access-Control-Allow-Origin", origin);            
        }
        res.addHeader("Access-Control-Allow-Methods", "*");
        String headers = req.getHeader("Access-Control-Request-Headers");
        // 支持所有自定義頭
        if (!org.springframework.util.StringUtils.isEmpty(headers)) {
            res.addHeader("Access-Control-Allow-Headers", headers);            
        }
        res.addHeader("Access-Control-Max-Age", "3600");
        // enable cookie
        res.addHeader("Access-Control-Allow-Credentials", "true");
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        // TODO Auto-generated method stub
    }

}

 (2)注冊自定攔截器CrosFilter,作用於全局:

package *;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;

import com.fasterxml.jackson.databind.ser.impl.FilteredBeanPropertyWriter;

@SpringBootApplication
public class AjaxserverApplication {

    public static void main(String[] args) {
        SpringApplication.run(AjaxserverApplication.class, args);
    }

    @Bean
    public FilterRegistrationBean registerFilter() {
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.addUrlPatterns("/*");
        bean.setFilter(new CrosFilter());
        return bean ;
    }
}

 (3)、創建接口服務層

package *;

import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping("/get")
    public ResultBean get() {
        System.out.println("TestController.get()");
        return new ResultBean("get ok");
    }

    @PostMapping("/postJson")
    public ResultBean postJson(@RequestBody User user) {
        System.out.println("TestController.postJson()");

        return new ResultBean("postJson " + user.getName());
    }

    @GetMapping("/getCookie")
    public ResultBean getCookie(@CookieValue(value = "cookie1") String cookie1) {
        System.out.println("TestController.getCookie()");
        return new ResultBean("getCookie " + cookie1);
    }
    
    @GetMapping("/getHeader")
    public ResultBean getHeader(@RequestHeader("x-header1") String header1,
            @RequestHeader("x-header2") String header2) {
        System.out.println("TestController.getHeader()");
        
        return new ResultBean("getHeader " + header1 + " " + header2);
    }
}

(4)、前端接口調用測試

jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000; // 請求的接口的前綴 // http://localhost:8080/test
var base = "/http://localhost:8080/test"; //測試模塊
describe("ajax跨越完全講解", function() {
  // 測試方法
  
it("get請求", function(done) {   // 服務器返回的結果 var result; $.getJSON(base + "/get").then(function(jsonObj) {   result = jsonObj; }); // 由於是異步請求,需要使用setTimeout來校驗 setTimeout(function() {   expect(result).toEqual({   "data" : "get ok" }); // 校驗完成,通知jasmine框架 done();  }, 100);
  });
  
  //此處測試采用的是
jasmine測試框架

   ......

});   

歡迎提問,共同學習......


免責聲明!

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



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