關(guān)于跨域無(wú)效的問(wèn)題及解決(java后端方案)
通用后端跨域方法
1、@CrossOrigin 注解
在Spring Boot 中給我們提供了一個(gè)注解 @CrossOrigin 來(lái)實(shí)現(xiàn)跨域,這個(gè)注解可以實(shí)現(xiàn)方法級(jí)別的細(xì)粒度的跨域控制。
我們可以在類或者方添加該注解,如果在類上添加該注解,該類下的所有接口都可以通過(guò)跨域訪問(wèn),如果在方法上添加注解,那么僅僅只限于加注解的方法可以訪問(wè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ù)約列表分頁(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 注解不生效問(wèn)題
在Spring框架4.2版本后,Spring給出了注解的方式解決問(wèn)題。
即在Controller控制器中,在Controller注解上方添加@CrossOrigin注解。
但是使用這種方式后也有可能仍然出現(xiàn)跨域問(wèn)題,解決方案就是:
- 在@RequestMapping注解中沒(méi)有指定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ù)約列表分頁(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 實(shí)現(xiàn)WebMvcConfigurer 實(shí)現(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、過(guò)濾器實(shí)現(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);
}
}
跨域不生效問(wèn)題
(1)、@Order(Ordered.HIGHEST_PRECEDENCE)如果有登錄攔截,要將跨域filter等級(jí)提升為最高優(yōu)先級(jí)
(2)、 response.setHeader(“Access-Control-Allow-Headers”, “");
- Access-Control-Allow-Headers: * 在部分客戶端上有兼容問(wèn)題,MDN中介紹 Access-Control-Allow-Headers: * 有兩重意思。
- 一個(gè)是在服務(wù)端設(shè)置Access-Control-Allow-Credentials: true的時(shí)候這個(gè) * 只會(huì)被客戶端當(dāng)做字符串 * (我們不希望的,會(huì)出錯(cuò)的)。
- 另一個(gè)是沒(méi)有這個(gè)設(shè)置則會(huì)被當(dāng)做通配符(我們希望的,不會(huì)出錯(cuò)的)。
- 猜測(cè)是客戶端對(duì)于 * 的實(shí)現(xiàn)上有兼容性問(wèn)題,所以建議不要這樣設(shè)置,用到什么設(shè)置什么最好,例如:Access-Control-Allow-Headers: Content-Type,X-Requested-With,Authorization。
(3)、 response.setHeader(“Access-Control-Allow-Origin”, "”)
//指定允許其他域名訪問(wèn) ‘Access-Control-Allow-Origin:http://172.80.0.206'//一般用法(,指定域,動(dòng)態(tài)設(shè)置),3是因?yàn)椴辉试S攜帶認(rèn)證頭和cookies //是否允許后續(xù)請(qǐng)求攜帶認(rèn)證信息(cookies),該值只能是true,否則不返回
(4)、 response.setHeader(“Access-Control-Allow-Methods”, “POST, GET, PATCH,OPTIONS, DELETE, PUT”);OPTIONS 在預(yù)檢請(qǐng)求復(fù)雜請(qǐng)求中也會(huì)使用到
(5)、 如果有spring security結(jié)合使用需要添加該過(guò)濾器
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity security) throws Exception {
security.csrf().disable();
security.headers().frameOptions().disable();
//加入過(guò)濾器
security.addFilterBefore(new CORSFilter(), UsernamePasswordAuthenticationFilter.class);
}
}4、定制化參數(shù)實(shí)現(xiàn)跨域
前面要么是*,實(shí)際需求是根據(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è)置哪個(gè)源可以訪問(wèn)
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è)置哪個(gè)方法可以訪問(wèn)
response.setHeader("Access-Control-Allow-Methods", allowMethods);
}
if (!StringUtils.isEmpty(allowCredentials)) {
// 允許攜帶cookie
response.setHeader("Access-Control-Allow-Credentials", allowCredentials);
}
if (!StringUtils.isEmpty(allowHeaders)) {
// 允許攜帶哪個(gè)頭
response.setHeader("Access-Control-Allow-Headers", allowHeaders);
}
if (!StringUtils.isEmpty(exposeHeaders)) {
// 允許攜帶哪個(gè)頭
response.setHeader("Access-Control-Expose-Headers", exposeHeaders);
}
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}5、 使用SpringCloud網(wǎng)關(guān)GateWay實(shí)現(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,否則會(huì)由于配置沖突導(dǎo)致依然出現(xiàn)跨域問(wèn)題
6、nginx配置代理解決跨域問(wèn)題
server {
listen 8000;
server_name localhost;
# / 表示匹配路徑為/的url
location / {
proxy_pass http://需要跨域的域名:5500;
}
# /user 表示訪問(wèn)以/user 開頭 的地址 如/username,/user/find等
location /user {
proxy_pass http://需要跨域的域名:3000;
}
}7、nginx配置響應(yīng)頭允許跨域
#
# Wide-open CORS config for nginx
#
location / {
#### 對(duì)OPTIONS請(qǐng)求,會(huì)設(shè)置很多的請(qǐng)求頭,并返回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é)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
詳解基于java的Socket聊天程序——初始設(shè)計(jì)(附demo)
本篇文章主要介紹了Socket聊天程序——初始設(shè)計(jì)(附demo),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-12-12
Java 實(shí)現(xiàn)Excel文檔添加超鏈接的代碼
超鏈接即內(nèi)容鏈接,通過(guò)給特定對(duì)象設(shè)置超鏈接,可實(shí)現(xiàn)載體與特定網(wǎng)頁(yè)、文件、郵件、網(wǎng)絡(luò)等的鏈接,點(diǎn)擊鏈接載體可打開鏈接目標(biāo),在文檔處理中是一種比較常用的功能,本文將介紹通過(guò)Java程序給Excel文檔添加超鏈接的方法,感興趣的朋友一起看看吧2020-02-02
SpringBoot中@ConfigurationProperties 配置綁定
本文主要介紹了SpringBoot中@ConfigurationProperties 配置綁定,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11
由ArrayList來(lái)深入理解Java中的fail-fast機(jī)制
fail-fast俗稱快速失敗,是在多線程進(jìn)行迭代操作時(shí)產(chǎn)生沖突的一種異常拋出機(jī)制,下面我們就由ArrayList來(lái)深入理解Java中的fail-fast機(jī)制.2016-05-05
Java設(shè)計(jì)模式之靜態(tài)代理模式實(shí)例分析
這篇文章主要介紹了Java設(shè)計(jì)模式之靜態(tài)代理模式,結(jié)合實(shí)例形式分析了靜態(tài)代理模式的概念、原理、定義與用法,需要的朋友可以參考下2018-04-04
在Spring MVC中處理請(qǐng)求參數(shù)的方法總結(jié)
在Spring MVC中處理請(qǐng)求參數(shù)是通過(guò)使用各種注解來(lái)實(shí)現(xiàn)的,本文給大家介紹了在Spring MVC中處理不同類型請(qǐng)求參數(shù)的方法,并通過(guò)代碼講解的非常詳細(xì),需要的朋友可以參考下2024-08-08
Spring boot詳解緩存redis實(shí)現(xiàn)定時(shí)過(guò)期方法
本篇文章分享的就是spring boot中的一個(gè)輪子,spring cache注解的方式實(shí)現(xiàn)接口數(shù)據(jù)緩存。默認(rèn)的配置想非常簡(jiǎn)單,但是有一個(gè)弊端是緩存數(shù)據(jù)為永久緩存,本次將介紹如何設(shè)置接口緩存數(shù)據(jù)的過(guò)期時(shí)間2022-07-07

