關(guān)于跨域無效的問題及解決(java后端方案)
通用后端跨域方法
1、@CrossOrigin 注解
在Spring Boot 中給我們提供了一個注解 @CrossOrigin 來實現(xiàn)跨域,這個注解可以實現(xiàn)方法級別的細粒度的跨域控制。
我們可以在類或者方添加該注解,如果在類上添加該注解,該類下的所有接口都可以通過跨域訪問,如果在方法上添加注解,那么僅僅只限于加注解的方法可以訪問。
@Slf4j @RestController @RequestMapping(value = AppPath.SERVICE_LOCATION_URL + "/appointment") @Api(value = "AppointmentController",tags = "預(yù)約列表接口") @CrossOrigin public class AppointmentController { @Autowired private LiveAppointmentService appointmentService; @RequestMapping @ApiOperation(value = "預(yù)約列表分頁查詢", response = CsLiveAppointmentDTO.class) public JsonResult<PageInfo> getAppointmentList(AppointmentListDTO dto){ log.info("getAppointmentList vo:{}", JSONUtil.toJsonStr(dto)); PageInfo<CsLiveAppointmentDTO> appointmentList = appointmentService.getAppointmentList(dto); return JsonResult.success(appointmentList); } }
@CrossOrigin 注解不生效問題
在Spring框架4.2版本后,Spring給出了注解的方式解決問題。
即在Controller控制器中,在Controller注解上方添加@CrossOrigin注解。
但是使用這種方式后也有可能仍然出現(xiàn)跨域問題,解決方案就是:
- 在@RequestMapping注解中沒有指定Get、Post方式,或者使用@GetMapping或者@Post Mapping
- 在@CrossOrigin(methods = {RequestMethod.POST})指定方法
@Slf4j @RestController @RequestMapping(value = AppPath.SERVICE_LOCATION_URL + "/appointment") @Api(value = "AppointmentController",tags = "預(yù)約列表接口") @CrossOrigin public class AppointmentController { @Autowired private LiveAppointmentService appointmentService; @ApiOperation(value = "預(yù)約列表分頁查詢", response = CsLiveAppointmentDTO.class) //@GetMapping("getList") @RequestMapping(method = RequestMethod.GET) public JsonResult<PageInfo> getAppointmentList(AppointmentListDTO dto){ log.info("getAppointmentList vo:{}", JSONUtil.toJsonStr(dto)); PageInfo<CsLiveAppointmentDTO> appointmentList = appointmentService.getAppointmentList(dto); return JsonResult.success(appointmentList); } }
2、springboot2.0 實現(xiàn)WebMvcConfigurer 實現(xiàn)跨域
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("POST","GET","OPTIONS") .allowedHeaders("*") .allowCredentials(false).maxAge(3600); } }
3、過濾器實現(xiàn)跨域
@WebFilter(filterName = "CorsFilter") @Configuration @Order(Ordered.HIGHEST_PRECEDENCE) @Slf4j public class CorsFilter implements Filter { @Value("${allow.headers:X-Requested-With,Authorization,Content-Type}") private String allowHeaders; @Value("${allow.origin:https://xxx.com}") private String allowOrigin; @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) res; // response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Origin", "http://xxx:9091"); response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Methods", "POST, GET, PATCH,OPTIONS, DELETE, PUT"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", allowHeaders); // response.setHeader("Access-Control-Allow-Headers", "*"); log.info("CorsFilter res {},{}", response.getHeader("Access-Control-Allow-Origin"), response.containsHeader("Access-Control-Allow-Origin")); chain.doFilter(req, res); } }
跨域不生效問題
(1)、@Order(Ordered.HIGHEST_PRECEDENCE)如果有登錄攔截,要將跨域filter等級提升為最高優(yōu)先級
(2)、 response.setHeader(“Access-Control-Allow-Headers”, “");
- Access-Control-Allow-Headers: * 在部分客戶端上有兼容問題,MDN中介紹 Access-Control-Allow-Headers: * 有兩重意思。
- 一個是在服務(wù)端設(shè)置Access-Control-Allow-Credentials: true的時候這個 * 只會被客戶端當做字符串 * (我們不希望的,會出錯的)。
- 另一個是沒有這個設(shè)置則會被當做通配符(我們希望的,不會出錯的)。
- 猜測是客戶端對于 * 的實現(xiàn)上有兼容性問題,所以建議不要這樣設(shè)置,用到什么設(shè)置什么最好,例如:Access-Control-Allow-Headers: Content-Type,X-Requested-With,Authorization。
(3)、 response.setHeader(“Access-Control-Allow-Origin”, "”)
//指定允許其他域名訪問 ‘Access-Control-Allow-Origin:http://172.80.0.206'//一般用法(,指定域,動態(tài)設(shè)置),3是因為不允許攜帶認證頭和cookies //是否允許后續(xù)請求攜帶認證信息(cookies),該值只能是true,否則不返回
(4)、 response.setHeader(“Access-Control-Allow-Methods”, “POST, GET, PATCH,OPTIONS, DELETE, PUT”);OPTIONS 在預(yù)檢請求復(fù)雜請求中也會使用到
(5)、 如果有spring security結(jié)合使用需要添加該過濾器
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity security) throws Exception { security.csrf().disable(); security.headers().frameOptions().disable(); //加入過濾器 security.addFilterBefore(new CORSFilter(), UsernamePasswordAuthenticationFilter.class); } }
4、定制化參數(shù)實現(xiàn)跨域
前面要么是*,實際需求是根據(jù)業(yè)務(wù)參數(shù)定制化
@WebFilter(filterName = "corsFilter", urlPatterns = "/*", initParams = {@WebInitParam(name = "allowOrigin", value = "*"), @WebInitParam(name = "allowMethods", value = "GET,POST,PUT,DELETE,OPTIONS"), @WebInitParam(name = "allowCredentials", value = "true"), @WebInitParam(name = "allowHeaders", value = "Content-Type,X-Token")}) public class CorsFilter implements Filter { private String allowOrigin; private String allowMethods; private String allowCredentials; private String allowHeaders; private String exposeHeaders; @Override public void init(FilterConfig filterConfig) throws ServletException { allowOrigin = filterConfig.getInitParameter("allowOrigin"); allowMethods = filterConfig.getInitParameter("allowMethods"); allowCredentials = filterConfig.getInitParameter("allowCredentials"); allowHeaders = filterConfig.getInitParameter("allowHeaders"); exposeHeaders = filterConfig.getInitParameter("exposeHeaders"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; if (!StringUtils.isEmpty(allowOrigin)) { if(allowOrigin.equals("*")){ // 設(shè)置哪個源可以訪問 response.setHeader("Access-Control-Allow-Origin", allowOrigin); }else{ List<String> allowOriginList = Arrays.asList(allowOrigin.split(",")); if (allowOriginList != null && allowOriginList.size() > 0) { String currentOrigin = request.getHeader("Origin"); if (allowOriginList.contains(currentOrigin)) { response.setHeader("Access-Control-Allow-Origin", currentOrigin); } } } } if (!StringUtils.isEmpty(allowMethods)) { //設(shè)置哪個方法可以訪問 response.setHeader("Access-Control-Allow-Methods", allowMethods); } if (!StringUtils.isEmpty(allowCredentials)) { // 允許攜帶cookie response.setHeader("Access-Control-Allow-Credentials", allowCredentials); } if (!StringUtils.isEmpty(allowHeaders)) { // 允許攜帶哪個頭 response.setHeader("Access-Control-Allow-Headers", allowHeaders); } if (!StringUtils.isEmpty(exposeHeaders)) { // 允許攜帶哪個頭 response.setHeader("Access-Control-Expose-Headers", exposeHeaders); } filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { } }
5、 使用SpringCloud網(wǎng)關(guān)GateWay實現(xiàn)跨域
原理和前面類似
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.reactive.CorsWebFilter; import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; import org.springframework.web.util.pattern.PathPatternParser; @Configuration public class CorsConfig { @Bean public CorsWebFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(Boolean.TRUE);//允許Cookie跨域 config.addAllowedMethod("*"); config.addAllowedOrigin("*"); config.addAllowedHeader("*");//不要設(shè)置成*,參考前面 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser()); source.registerCorsConfiguration("/**", config); return new CorsWebFilter(source); } }
注:在下層服務(wù)不需要在做任何跨域配置,例如注解@CrossOrigin,否則會由于配置沖突導(dǎo)致依然出現(xiàn)跨域問題
6、nginx配置代理解決跨域問題
server { listen 8000; server_name localhost; # / 表示匹配路徑為/的url location / { proxy_pass http://需要跨域的域名:5500; } # /user 表示訪問以/user 開頭 的地址 如/username,/user/find等 location /user { proxy_pass http://需要跨域的域名:3000; } }
7、nginx配置響應(yīng)頭允許跨域
# # Wide-open CORS config for nginx # location / { #### 對OPTIONS請求,會設(shè)置很多的請求頭,并返回204 if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; # # Custom headers and headers various browsers *should* be OK with but aren't # add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; # # Tell client that this pre-flight info is valid for 20 days # add_header 'Access-Control-Max-Age' 1728000; add_header 'Content-Type' 'text/plain; charset=utf-8'; add_header 'Content-Length' 0; return 204; } if ($request_method = 'POST') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; } if ($request_method = 'GET') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; } }
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
詳解基于java的Socket聊天程序——初始設(shè)計(附demo)
本篇文章主要介紹了Socket聊天程序——初始設(shè)計(附demo),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-12-12SpringBoot中@ConfigurationProperties 配置綁定
本文主要介紹了SpringBoot中@ConfigurationProperties 配置綁定,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-11-11由ArrayList來深入理解Java中的fail-fast機制
fail-fast俗稱快速失敗,是在多線程進行迭代操作時產(chǎn)生沖突的一種異常拋出機制,下面我們就由ArrayList來深入理解Java中的fail-fast機制.2016-05-05在Spring MVC中處理請求參數(shù)的方法總結(jié)
在Spring MVC中處理請求參數(shù)是通過使用各種注解來實現(xiàn)的,本文給大家介紹了在Spring MVC中處理不同類型請求參數(shù)的方法,并通過代碼講解的非常詳細,需要的朋友可以參考下2024-08-08Spring boot詳解緩存redis實現(xiàn)定時過期方法
本篇文章分享的就是spring boot中的一個輪子,spring cache注解的方式實現(xiàn)接口數(shù)據(jù)緩存。默認的配置想非常簡單,但是有一個弊端是緩存數(shù)據(jù)為永久緩存,本次將介紹如何設(shè)置接口緩存數(shù)據(jù)的過期時間2022-07-07