一、什么是跨域
(1)跨域
由於瀏覽器同源策略,凡是發送請求url的協議、域名、端口三者之間任意一個與當前頁面地址不同即為跨域。存在跨域的情況:
網絡協議不同,如http協議訪問https協議。 端口不同,如80端口訪問8080端口。 域名不同,如qianduanblog.com訪問baidu.com。 子域名不同,如abc.qianduanblog.com訪問def.qianduanblog.com。 域名和域名對應ip,如www.a.com訪問20.205.28.90.
(2)url格式
一般格式:協議 + 域名(子域名 + 主域名) + 端口號 + 資源地址
示例:https://www.dustyblog.cn:8080/say/Hello
是由https
+ www
+ dustyblog.cn
+ 8080
+ say/Hello
組成。
只要協議,子域名,主域名,端口號這四項組成部分中有一項不同,就可以認為是不同的域,不同的域之間互相訪問資源,就被稱之為跨域。
(3)Cors
CORS
全稱為Cross Origin Resource Sharing
(跨域資源共享), 每一個頁面需要返回一個名為Access-Control-Allow-Origin
的http頭來允許外域的站點訪問,你可以僅僅暴露有限的資源和有限的外域站點訪問。
我們可以理解為:如果一個請求需要允許跨域訪問,則需要在http頭中設置Access-Control-Allow-Origin來決定需要允許哪些站點來訪問。如假設需要允許https://www.dustyblog.c這個站點的請求跨域,則可以設置:
Access-Control-Allow-Origin:https://www.dustyblog.cn。
二、后端SpringBoot解決
1、方案一:CORS局部配置-使用@CrossOrigin注解
(1)在Controller
上使用@CrossOrigin
注解
該類下的所有接口都可以通過跨域訪問
@RequestMapping("/demo2") @RestController //@CrossOrigin //所有域名均可訪問該類下所有接口 @CrossOrigin("https://blog.csdn.net") // 只有指定域名可以訪問該類下所有接口 public class CorsTest2Controller { @GetMapping("/sayHello") public String sayHello() { return "hello world --- 2"; } }
這里指定當前的CorsTest2Controller
中所有的方法可以處理https://csdn.net
域上的請求,其他域名不能請求
(2)在方法上使用@CrossOrigin
注解
@RequestMapping("/preUser") @CrossOrigin("https://blog.csdn.net")
public Map<String,String> preUser(){
Map<String,String> map = new HashMap<String,String>();
map.put("preUser","success");
return map;
}
這里指定只有該方法可以處理https://csdn.net
域上的請求,其他域名不能請求
2、方案二:CORS局部配置-手工設置響應頭(局部跨域)
@RequestMapping("/hello") @ResponseBody public String index(HttpServletResponse response){ response.addHeader("Access-Control-Allow-Origin", "http://localhost:8080"); return "Hello World"; }
Access-Control-Allow-Origin 表示允許哪些原始域進行跨域訪問。 Access-Control-Allow-Credentials表示是否允許客戶端獲取用戶憑據。 Access-Control-Allow-Methods 表示允許哪些跨域請求的提交方式。(例如GET/POST) Access-Control-Expose-Headers 表示允許暴露哪些頭部信息給客戶端。 Access-Control-Max-Age 表示預檢請求 [Preflight Request] 的最大緩存時間。
3、方案三:CORS
全局配置-實現WebMvcConfigurer(本人習慣用法)
Spring Boot 2.0中已經廢棄WebMvcConfigurerAdapter類, 開發人員可以通過實現WebMvcConfigurer
接口實現相應的功能。
CorsConfig.java
:
/** * 跨域配置 */ @Configuration public class CorsConfig implements WebMvcConfigurer { @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**"). allowedOrigins("https://www.dustyblog.cn"). //允許跨域的域名,可以用*表示允許任何域名使用 allowedMethods("*"). //允許任何方法(post、get等) allowedHeaders("*"). //允許任何請求頭 allowCredentials(true). //帶上cookie信息 exposedHeaders(HttpHeaders.SET_COOKIE).maxAge(3600L); //maxAge(3600)表明在3600秒內,不需要再發送預檢驗請求,可以緩存該結果 } }; } }
4、方案四:CORS
全局配置-使用Filter方式進行設置(過濾器實現)
通過實現Fiter
接口在請求中添加一些Header
來解決跨域的問題,向請求端設置Response Header(響應頭部)的Access-Control-Allow-Origin屬性聲明允許跨域訪問。
@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 destroy() { } @Override public void init(FilterConfig filterConfig) throws ServletException { } }
5、方案五:CORS
全局配置-繼承使用Spring Web中的CorsFilter
package com.garyond.hurricane.config; import org.springframework.stereotype.Component; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; import java.util.Arrays; /** * 跨域訪問配置 * * @author Garyond */ @Component public class CustomCorsFilter extends CorsFilter { public CustomCorsFilter() { super(configurationSource()); } private static UrlBasedCorsConfigurationSource configurationSource() { CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOrigin("*"); config.addAllowedHeader("*"); config.setMaxAge(36000L); config.setAllowedMethods(Arrays.asList("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/v1/**", config); return source; } }
或者
package com.ranxx.conf; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; /** * 跨域問題 * * @author mousejoo */ @Configuration public class CorsConfig { private CorsConfiguration buildConfig() { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.addAllowedOrigin("*"); // 1 允許任何域名使用 corsConfiguration.addAllowedHeader("*"); // 2 允許任何頭 corsConfiguration.addAllowedMethod("*"); // 3 允許任何方法(post、get等) return corsConfiguration; } @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", buildConfig()); // 4 return new CorsFilter(source); } }
或者
package com.example.longecological.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; @Configuration public class GlobalCorsConfig { @Bean public CorsFilter corsFilter() { //1.添加CORS配置信息 CorsConfiguration config = new CorsConfiguration(); //放行哪些原始域 config.addAllowedOrigin("*"); //是否發送Cookie信息 config.setAllowCredentials(true); //放行哪些原始域(請求方式) config.addAllowedMethod("*"); //放行哪些原始域(頭部信息) config.addAllowedHeader("*"); //暴露哪些頭部信息(因為跨域訪問默認不能獲取全部頭部信息) config.addExposedHeader("content-type"); //2.添加映射路徑 UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource(); configSource.registerCorsConfiguration("/**", config); //3.返回新的CorsFilter. return new CorsFilter(configSource); } }
三、前端Vue.js解決
解決方案:采用proxyTable
配置代理跨域
(1)打開項目根目錄下的config/index.js
文件,參考以下proxyTable
部分的配置:
module.exports = {
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
// *********代理跨域 主要代碼**********
proxyTable: {
'/api': { //匹配接口路徑中的 /api,可以隨意命名
target: 'http://********:9001/api', //目標接口地址,設置調用的接口域名和端口號 別忘了加http、https
changeOrigin: true, //是否跨域
secure: true, // 允許https請求,https必須添加這個設置
pathRewrite: {
'^/api': '' // 路徑重寫 將 target 中的目標接口地址重寫為 /api,這里理解成用‘/api’代替target里面的地址,后面組件中我們掉接口時直接用api代替 比如我要調用'http://40.00.100.100:3002/user/add',直接寫‘/api/user/add’即可
} } }, }
注意:每次修改配置文件需要重啟服務才能生效:npm run dev
(2)通過axios來實現發送訪問
在main.js中導入已安裝好的axios,並掛載到原型上
import Axios from 'axios' //導入axios //將axios掛載到原型上 Vue.prototype.$axios = Axios;
(3)通過this.$axios.get().then()來實現數據請求
//發送get請求 show() { //用/api來代理'http://localhost:8083' this.$axios .get("/api/selectall") .then(res => { this.list = res.data.result; // } }) .catch(e => { console.log(e); }); }, //發送post請求 add() { this.$axios({ method: "post", url: "/api/saveinfo", params: { name: this.name //傳遞的參數 } }).then(res => { this.show(); }); },
注意:此種跨域解決方案,只能適用於測試階段,因為項目打包發布之后,由於proxyTable不能再使用,所以無法跨域了。正式環境中,前端項目和后端項目放在一起就不存在跨域問題了。如果非要跨域,則可以采取上面的后端解決方法或者下面的Nginx替代前端的proxyTable代理功能。
三、Nginx解決生產環境跨域問題
1、安裝好Nginx之后,打開安裝目錄下的conf/nginx.conf
文件,參考以下server
的簡單配置:
server { listen 9091; #監聽的端口 server_name localhost; #服務器名稱 #charset koi8-r; #access_log logs/host.access.log main; location / { root E:\hld-work\university\dist; #項目物理路徑 index index.html index.htm; #項目的默認首頁 } location /api { #檢測並替換接口內的字符串 proxy_pass http://**********:9001/api; #跨域訪問的目標URL proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; client_max_body_size 100m; } }
注:每次修改配置文件需要重啟服務才能生效
2、Nginx常用相關命令:
需要在Nginx文件目錄下運行命令行工具
(1)啟動
start nginx 或 nginx.exe
(2)停止
nginx.exe -s stop 或 ginx.exe -s quit
注:stop是快速停止nginx,可能並不保存相關信息;quit是完整有序的停止nginx,並保存相關信息。
(3)重新載入
nginx.exe -s reload
(4)重新打開日志文件
nginx.exe -s reopen
(5)查看版本
nginx -v