為什么會跨域,要先了解瀏覽器的同源策略SOP(Same Orign Policy) https://segmentfault.com/a/1190000015597029
同源:
如果兩個頁面的協議,端口(如果有指定)和主機都相同,則兩個頁面具有相同的源。
協議/主機/端口
跨源網絡訪問
同源策略控制了不同源之間的交互,例如在使用XMLHttpRequest時會受到同源策略的約束。即同源策略限制了從一個源加載的文檔或者腳本如何與另一個源的資源進行交互。這是一個用於隔離潛在惡意文件的重要安全機制。
如果沒有同源策略限制的接口請求則容易被CSRF攻擊,下面是CSRF攻擊的原理:
CORS
CORS是一個W3C標准,全稱是"跨域資源共享"(Cross-origin resource sharing)。
它允許瀏覽器向跨源服務器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。
CORS需要瀏覽器和服務器同時支持。
- 所有瀏覽器都支持該功能,IE瀏覽器不能低於IE10。
整個CORS通信過程,都是瀏覽器自動完成,不需要用戶參與。 對於開發者來說,CORS通信與同源的AJAX通信沒有差別,代碼完全一樣。瀏覽器一旦發現AJAX請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感覺。 - 實現CORS通信的關鍵是服務器。只要服務器實現了CORS接口,就可以跨源通信。
即CORS與普通請求代碼一樣。
CORS與JSONP相比
- JSONP只能實現GET請求,而CORS支持所有類型的HTTP請求。
- 使用CORS,開發者可以使用普通的XMLHttpRequest發起請求和獲得數據,比起JSONP有更好的錯誤處理。
- JSONP主要被老的瀏覽器支持,它們往往不支持CORS,而絕大多數現代瀏覽器都已經支持了CORS。
CORS全稱為Cross Origin Resource Sharing(跨域資源共享), 每一個頁面需要返回一個名為Access-Control-Allow-Origin的http頭來允許外域的站點訪問,你可以僅僅暴露有限的資源和有限的外域站點訪問。
我們可以理解為:如果一個請求需要允許跨域訪問,則需要在http頭中設置Access-Control-Allow-Origin來決定需要允許哪些站點來訪問,如要允許www.baidu.com這個站點的請求跨域,則可以設置:
Access-Control-Allow-Origin:http://www.baidu.com
CROS跨域常用header
Access-Control-Allow-Origin: 允許哪些ip或域名可以跨域訪問
Access-Control-Max-Age: 表示在多少秒之內不需要重復校驗該請求的跨域訪問權限
Access-Control-Allow-Methods: 表示允許跨域請求的HTTP方法,如:GET、POST、PUT、DELETE
Access-Control-Allow-Headers: 表示訪問請求中允許攜帶哪些Header信息,如:Accept、Accept-Language、Content- Language、Content-Type
spring中解決跨域問題的方案:
1、@CrossOrigin注解
此注解既可用於方法也可用於類
示例如下:
@CrossOrigin(origins = "http://www.zhihu.com") @RequestMapping(value = "/allProductions", method = RequestMethod.GET) public Result getAllOldProductions() { }
@CrossOrigin
注解既可注解在方法上,也可注解在類上。
2、XML全局配置
所有跨域請求都可以訪問
<mvc:cors> <mvc:mapping path="/**" /> </mvc:cors>
更加細粒度的配置:
<mvc:cors> <mvc:mapping path="/api/**" allowed-origins="https://domain1.com, https://domain2.com" allowed-methods="GET, PUT" allowed-headers="header1, header2, header3" exposed-headers="header1, header2" allow-credentials="true" max-age="123" /> <mvc:mapping path="/resources/**" allowed-origins="https://domain1.com" /> </mvc:cors>
Springboot解決跨域問題配置
1、使用@CrossOrigin注解,同上
2、使用CorsFilter進行全局跨域配置
@Configuration public class GlobalCorsConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration corsConfig = new CorsConfiguration(); // 開放哪些ip、端口、域名的訪問權限,星號表示開放所有域 corsConfig.addAllowedOrigin("*"); // 允許發送cookie信息 corsConfig.setAllowCredentials(true); // 允許get,post方法 corsConfig.addAllowedMethod(HttpMethod.POST); corsConfig.addAllowedMethod(HttpMethod.GET); // 允許HTTP請求頭中攜帶哪些header信息 corsConfig.addAllowedHeader("*"); // 暴漏哪些頭部信息(因為跨域訪問默認不能獲取全部頭部信息) corsConfig.addExposedHeader("Content-Type"); // 添加映射路徑,“/**”表示對所有的路徑實行全局跨域訪問權限的設置 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", corsConfig); return new CorsFilter(source); } }
3、重寫WebMvcConfigurer的addCorsMappings方法(全局跨域配置)
@Bean public WebMvcConfigurer crosConfigurer() { return new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") //對哪些映射路徑應用跨域配置 .allowedOrigins("*") // 允許哪些域名訪問 .allowCredentials(true) .allowedMethods("GET", "POST") .allowedHeaders("*") .exposedHeaders("Content-Type"); } }; }
或者:
import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class GlobalCorsConfig implements WebMvcConfigurer{ @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("HEAD", "GET", "POST", "PUT", "DELETE", "OPTIONS") .allowCredentials(true) .maxAge(3600) .allowedHeaders("*"); } }
4、過濾器實現
@Component public class CorsFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletResponse res = (HttpServletResponse) response; res.addHeader("Access-Control-Allow-Credentials", "true"); res.addHeader("Access-Control-Allow-Origin", "*"); res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT"); res.addHeader("Access-Control-Allow-Headers", "Content-Type,X-CAF-Authorization-Token,sessionToken,X-TOKEN"); if (((HttpServletRequest) request).getMethod().equals("OPTIONS")) { response.getWriter().println("ok"); return; } chain.doFilter(request, response); } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void destroy() { } }