1、原因
浏览器【同源策略】,要求host,port必须相同。
2、解决
前置,逻辑:浏览器【预检机制】。确认目标服务器是否支持跨域访问
但是满足以下条件不会发送跨域请求
a. 方法是get ,post,head 任意一个
b. 请求头包含accept,accept-language,content-language,content-type,dpr,downlink,save-data,viewport.width,width
c. Content-type 值为 text/plain,multipart/formdata,application/x-ww-form-urlencoded
整体逻辑:1、先发送预检请求,2、如果确认可以跨域访问,再次发送真正的url请求。
方法:在后端服务添加cors策略的配置就可以了。
1、在web项目根目录下添加corssdomain.xml文件
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<site-control permitted-cross-domain-policies="master-only"/>
<!-- 允许example.com及其子域访问 -->
<allow-access-from domain="*.example.com"/>
<!-- 允许http://www.example.com访问 -->
<allow-access-from domain="www.example.com"/>
<allow-http-request-headers-from domain="*.csdn.net" headers="*"/>
</cross-domain-policy>
2、spring项目添加过滤器
public class CORSFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpServletRequest request = (HttpServletRequest) servletRequest;
String origin = request.getHeader("Origin");
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Headers", "Content-Type,dsf-token");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,HEAD,PUT");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
3、springboot项目
controller配置 crossOrigin
@CrossOrigin(origins = "http://example.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
...
}
全局配置
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}
spring-secrity
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()...
}
}
添加拦截器
package com.yaoct.blog.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class CrossOriginInteceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String origin = request.getHeader("Origin");
System.out.println(origin);
//当成非跨域请求
if(origin==null||origin.equals("")){
return true;
}
//非简单请求需要预请求,有json有cookie都是非简单请求
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", "content-type");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
过滤器
/**
* 跨域配置,springboot2.4.0之后的版本不能用,因为多了层校验(设置Credentials为true时不能同时设置跨越地址为*)
* * C - Cross O - Origin R - Resource S - Sharing
*/
@Configuration
public class CorsConfig {
@Bean
public FilterRegistrationBean corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
// 允许cookies跨域
config.setAllowCredentials(true);
// 需要跨域的地址 * 表示对所有的地址都可以访问
//When allowCredentials is true, allowedOrigins cannot contain the special value "*"
// since that cannot be set on the "Access-Control-Allow-Origin" response header.
// To allow credentials to a set of origins, list them explicitly or consider using "allowedOriginPatterns" instead
// config.setAllowedOrigins(Collections.singletonList("*"));
config.addAllowedOriginPattern("*");
// 跨域的请求头, *表示全部
config.setAllowedHeaders(Collections.singletonList("*"));
// 跨域的请求方法, *表示全部允许,也可以单独设置GET、PUT等
config.setAllowedMethods(Collections.singletonList("*"));
List<String> list = new ArrayList<>();
list.add("Content-Disposition");
config.setExposedHeaders(list);
// 预检请求的缓存时间(秒), 即在这个时间段里,对于相同的跨域请求不会再预检了
config.setMaxAge(300L);
// 配置可以访问的地址
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean registrationBean = new FilterRegistrationBean(new CorsFilter(source));
registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registrationBean;
}
}