Table of Contents
二、繼承 HandlerInterceptorAdapter
引言
我們在開發(fā)過程中經(jīng)常會遇到前后端分離而導(dǎo)致的跨域問題,導(dǎo)致無法獲取返回結(jié)果。跨域就像分離前端和后端的一道鴻溝,君在這邊,她在那邊,兩兩不能往來.
什么是跨域(CORS)
跨域(CORS)是指不同域名之間相互訪問??缬?,指的是瀏覽器不能執(zhí)行其他網(wǎng)站的腳本,它是由瀏覽器的同源策略所造成的,是瀏覽器對于JavaScript所定義的安全限制策略。
什么情況會跨域
同一協(xié)議, 如http或https
同一IP地址, 如127.0.0.1
同一端口, 如8080
以上三個條件中有一個條件不同就會產(chǎn)生跨域問題。
解決方案
前端解決方案
使用JSONP方式實現(xiàn)跨域調(diào)用;
使用NodeJS服務(wù)器做為服務(wù)代理,前端發(fā)起請求到NodeJS服務(wù)器, NodeJS服務(wù)器代理轉(zhuǎn)發(fā)請求到后端服務(wù)器;
后端解決方案
nginx反向代理解決跨域
服務(wù)端設(shè)置Response Header(響應(yīng)頭部)的Access-Control-Allow-Origin
在需要跨域訪問的類和方法中設(shè)置允許跨域訪問(如Spring中使用@CrossOrigin注解);
繼承使用Spring Web的CorsFilter(適用于Spring MVC、Spring Boot)
實現(xiàn)WebMvcConfigurer接口(適用于Spring Boot)
具體方式
一、使用Filter方式進(jìn)行設(shè)置
使用Filter過濾器來過濾服務(wù)請求,向請求端設(shè)置Response Header(響應(yīng)頭部)的Access-Control-Allow-Origin屬性聲明允許跨域訪問。
@WebFilterpublicclassCorsFilterimplementsFilter{@OverridepublicvoiddoFilter(ServletRequest req, ServletResponse res, FilterChain chain)throwsIOException, ServletException{? ? ? ? ? HttpServletResponse response = (HttpServletResponse) res;? ? ? ? ? response.setHeader("Access-Control-Allow-Origin","*");? ? ? ? ? response.setHeader("Access-Control-Allow-Methods","*");? ? ? ? ? response.setHeader("Access-Control-Max-Age","3600");? ? ? ? ? response.setHeader("Access-Control-Allow-Headers","*");? ? ? ? response.setHeader("Access-Control-Allow-Credentials","true");? ? ? ? chain.doFilter(req, res);? ? ? }? }復(fù)制代碼
二、繼承 HandlerInterceptorAdapter
@ComponentpublicclassCrossInterceptorextendsHandlerInterceptorAdapter{@OverridepublicbooleanpreHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throwsException{? ? ? ? response.setHeader("Access-Control-Allow-Origin","*");? ? ? ? response.setHeader("Access-Control-Allow-Methods","GET, POST, PUT, DELETE, OPTIONS");? ? ? ? response.setHeader("Access-Control-Max-Age","3600");? ? ? ? response.setHeader("Access-Control-Allow-Headers","*");? ? ? ? response.setHeader("Access-Control-Allow-Credentials","true");returntrue;? ? }}復(fù)制代碼
三、實現(xiàn) WebMvcConfigurer
@Configuration@SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")publicclassAppConfigimplementsWebMvcConfigurer{@OverridepublicvoidaddCorsMappings(CorsRegistry registry){? ? ? ? registry.addMapping("/**")// 攔截所有的請求.allowedOrigins("http://www.abc.com")// 可跨域的域名,可以為 *.allowCredentials(true)? ? ? ? ? ? ? ? .allowedMethods("*")// 允許跨域的方法,可以單獨配置.allowedHeaders("*");// 允許跨域的請求頭,可以單獨配置}}復(fù)制代碼
四、使用Nginx配置
location / {? add_header Access-Control-Allow-Origin *;? add_header Access-Control-Allow-Headers X-Requested-With;? add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;? if ($request_method = 'OPTIONS') {? ? return 204;? }}復(fù)制代碼
五、使用@CrossOrgin注解
如果只是想部分接口跨域,且不想使用配置來管理的話,可以使用這種方式
在Controller使用
@CrossOrigin@RestController@RequestMapping("/user")public class UserController {@GetMapping("/{id}")public User get(@PathVariable Long id) {}@DeleteMapping("/{id}")public void remove(@PathVariable Long id) {}}復(fù)制代碼
在具體接口上使用
@RestController@RequestMapping("/user")public class UserController {@CrossOrigin@GetMapping("/{id}")public User get(@PathVariable Long id) {}@DeleteMapping("/{id}")public void remove(@PathVariable Long id) {}}復(fù)制代碼
Spring Cloud Gateway 跨域配置
spring:cloud:gateway:globalcors:cors-configurations:'[/**]':# 允許跨域的源(網(wǎng)站域名/ip),設(shè)置*為全部# 允許跨域請求里的head字段,設(shè)置*為全部# 允許跨域的method, 默認(rèn)為GET和OPTIONS,設(shè)置*為全部allow-credentials:trueallowed-origins:-"http://xb.abc.com"-"http://sf.xx.com"allowed-headers:"*"allowed-methods:-OPTIONS-GET-POST-DELETE-PUT-PATCHmax-age:3600復(fù)制代碼
注意:通過gateway轉(zhuǎn)發(fā)的其他項目,不要進(jìn)行配置跨域配置
有時即使配置了也不會起作用,這時你可以根據(jù)瀏覽器控制的錯誤輸出來查看問題,如果提示是response中 header 出現(xiàn)了重復(fù)的Access-Control-*請求頭,可以進(jìn)行如下操作
importjava.util.ArrayList;
importorg.springframework.cloud.gateway.filter.GatewayFilterChain;
importorg.springframework.cloud.gateway.filter.GlobalFilter;
importorg.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
importorg.springframework.core.Ordered;
importorg.springframework.http.HttpHeaders;
importorg.springframework.stereotype.Component;importorg.springframework.web.server.ServerWebExchange;
importreactor.core.publisher.Mono;@Component("corsResponseHeaderFilter")publicclassCorsResponseHeaderFilterimplementsGlobalFilter,Ordered{@OverridepublicintgetOrder()
{// 指定此過濾器位于NettyWriteResponseFilter之后// 即待處理完響應(yīng)體后接著處理響應(yīng)頭returnNettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER +1;? }
@OverridepublicMonofilter(ServerWebExchange exchange, GatewayFilterChain chain){returnchain.filter(exchange).then(Mono.defer(() -> {? ? ? exchange.getResponse().getHeaders().entrySet().stream()? ? ? ? ? .filter(kv -> (kv.getValue() !=null&& kv.getValue().size() >1))? ? ? ? ? .filter(kv -> (? ? ? ? ? ? ? kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)? ? ? ? ? ? ? ? ? || kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)? ? ? ? ? ? ? ? ? || kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)? ? ? ? ? ? ? ? ? || kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS)? ? ? ? ? ? ? ? ? || kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_MAX_AGE)))? ? ? ? ? .forEach(kv -> {? ? ? ? ? ? kv.setValue(newArrayList() {{? ? ? ? ? ? ? add(kv.getValue().get(0));? ? ? ? ? ? }});? ? ? ? ? });returnchain.filter(exchange);? ? }));? }}