1. 程式人生 > 程式設計 >包教不包會系列-跨域

包教不包會系列-跨域

前言

這段時間心態上有點放鬆了,收收心了

昨天晚上無聊,把 SpringBoot 的官方檔案看了一遍,對於一個英語渣來說,真難得

想學的東西很多,但是時間不允許啊。前端 Or 後端,早日決斷吧 ???,太難了

跨域

跨域解決的方案有好幾種,掌握 Cors 就行了,別的瞭解下就可以了

什麼情況下回跨域呢?

跨域是指從一個源去請求另一個源的資源,瀏覽器基於完全考慮並遵循同源策略,禁止跨域訪問。但是我們可以通過一些手段(jsonp 或者 Cors)來實現跨域。

簡單理解:當 url 中的協議/域名/埠 不同時,就產生了跨域。

跨域只會發生在瀏覽器中,後端服務之間的介面呼叫是沒有跨域一說的

跨域的解決方案?

  • nginx 反向代理,將請求的介面全部轉發就行了
  • jsonp
  • cors

nginx 反向代理解決跨域

location /api {
    proxy_pass  http://192.168.202.50:8082/;
}

匹配 url 中以 /api 開頭的路徑
http://192.168.202.50:8081/api -->  http://192.168.202.50:8082
http://192.168.202.50:8081/api/users/2 --> http://192.168.202.50:8082/users/2 
複製程式碼

來自 8081 的頁面請求了 /api/users/2 代理介面,nginx 經過路徑匹配,找到對應的 location 處理,將其轉發到實際介面 /8082/users/2,從而規避跨域。

jsonp 解決跨域

jsonp 解決跨域了跨域,但是也有自己的缺陷,服務端需要對跨域和非跨域做不同的處理。

$.ajax({
    url:'http:192.168.202.50:8082/users/2'
    type:'get',dataType:'jsonp',success(data){},error(error){}
});
複製程式碼

在傳送請求的時候,瀏覽器會多攜帶一個 callback 引數

// 服務端使用 SpringBoot 處理
@GetMapping(value = "/users/2")
public String domainAjaxJsonpNoCookie
(HttpServletRequest request)
{ HashMap<Object,Object> ret = new HashMap<>(16); ret.put("key1","ajax-jsonp-no-cookie"); ret.put("key2",Math.random()); String callback = request.getParameter("callback"); String retString= JSON.toJSONString(ret); // 跨域處理,當不是跨域的時候,這樣處理ajax 會拿不到正確的資料 retString=callback+"("+retString+")"; return retString; } 複製程式碼

Cors

推薦看下 阮一峰-跨域資源共享 CORS 詳解

cors 解決跨域,只需要服務端配合就行,瀏覽器會自動判斷是否跨域,新增 origin 請求頭,服務端只需要新增請求頭 Access-Control-Allow-Origin:* (這樣 cookie 不能攜帶) Access-Control-Allow-Origin:http://192.168.202.8080 (這樣可以攜帶 cookie)

SpringBoot 使用註解 @CrossOrigin

// 註解可以新增到類上或者方法上
@CrossOrigin(value = "http://192.168.202.50:8080",allowCredentials="true")
@GetMapping(value = "/domain/cors/cookie")
public Map domainCorsCookie(HttpServletRequest request,HttpServletResponse response){
    Cookie[] cookies = request.getCookies();
    HashMap<Object,Object> ret = new HashMap<>(16);
    if(cookies!=null){
        for (Cookie cookie1 : cookies) {
            if (Objects.equals("corsCookie",cookie1.getName())) {
                ret.put(cookie1.getName(),cookie1.getValue());
            }
        }
    }
    return ret;
}
複製程式碼

CorsFilter 過濾器

@Configuration
public class MyCorsConfig {
    @Autowired
    private AppProperties appProperties;
    /**
     * 配置 cors 過濾器
     */
    private CorsConfigurationSource getMyCorsConfigurationSource(String path){
        CorsConfiguration config = new CorsConfiguration();
        // 設定跨域允許攜帶 cookie
        config.setAllowCredentials(true);
        // 配置允許那些 網址跨域
        config.setAllowedOrigins(appProperties.getAllowIp());
        // 配置多長時間不用跨域預檢請求
        config.setMaxAge(1800L);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration(path,config);
        return source;
    }

    @Bean
    public FilterRegistrationBean  registrationCorsFilterBean(){
        FilterRegistrationBean filterRegistrationBean =new FilterRegistrationBean();
        // 所有的請求都允許跨域
        CorsConfigurationSource myCorsConfigurationSource = getMyCorsConfigurationSource("/**");
        CorsFilter corsFilter = new CorsFilter(myCorsConfigurationSource);
        filterRegistrationBean.setFilter(corsFilter);
        return filterRegistrationBean;
    }
}

@Configuration
@ConfigurationProperties(prefix = "app.cors")
@Data
public class AppProperties {
    private List<String> allowIp;
}
複製程式碼

遇到的坑

  • 跨域的時候 cookie 不能攜帶

這個需要客戶端和服務端同時配合,服務端才能拿到 cookie

$.ajax({
    url:'http:192.168.202.50:8082/users/2'
    type:'get',// 告訴瀏覽器要攜帶 cookie
    xhrFields: {
        withCredentials: true
    },error(error){}
});
複製程式碼
// Access-Control-Allow-Origin:http://192.168.202.8080,
// 這個屬性需要設定對應的地址,設定 * 不行
// allowCredentials  服務端配置允許攜帶 cookie
@CrossOrigin(value = "http://192.168.202.50:8080",allowCredentials="true")
複製程式碼
// 配置過濾器屬性,Access-Control-Allow-Origin 和 allowCredentials
 private CorsConfigurationSource getMyCorsConfigurationSource(String path){
        CorsConfiguration config = new CorsConfiguration();
        // 設定跨域允許攜帶 cookie
        config.setAllowCredentials(true);
        // 配置允許那些 網址跨域
        config.setAllowedOrigins(appProperties.getAllowIp());
        // 配置多長時間不用跨域預檢請求
        config.setMaxAge(1800L);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration(path,config);
        return source;
    }
複製程式碼

cookie Path 設定錯誤

上圖只能看到當前頁面可以訪問的 cookie

檢視域名下所有的 cookie

進入下級頁面即可看到 cookie 選項

可以檢視某個域名下的全部 cookie

        Cookie retCookie=new Cookie("serverCookie",String.valueOf(Math.random()));
        retCookie.setMaxAge(10000000);
        retCookie.setHttpOnly(true);
        // 跨域設定 cookie 必須設定 path
//        retCookie.setPath("/");
        response.addCookie(retCookie);
複製程式碼

當你沒有設定 Path 的時候,預設設定請求介面路徑。

比如說,你登陸介面(/login)返回 cookie(name=login),cookie 沒有設定 path,會自動給你設定 /login。而當你請求/users/2 的介面的時候,是不會攜帶 cookie(name=login)。