一、跨域问题出现的原因
跨域是指a页面想获取b页面资源,如果a、b页面的协议、域名、端口、子域名不同,或是a页面为ip地址,b页面为域名地址,所进行的访问行动都是跨域的,而浏览器为了安全问题一般都限制了跨域访问,也就是不允许跨域请求资源。
跨域问题的根本原因:因为浏览器收到同源策略的限制,当前域名的js只能读取同域下的窗口属性。什么叫做同源策略?同源策略是浏览器的一个安全限制,要求域名、协议、端口相同,如果不同则没办法进行数据交互。而跨域配置,则是为了解除这方面的限制。
配置选项简介
• Access-Control-Allow-Origin: 允许访问的域名
• Access-Control-Allow-Methods: 允许访问的请求方式
• Access-Control-Allow-Headers: 允许使用的 Header
• Access-Control-Allow-Credentials: 是否允许用户发送、处理 Cookie
• Access-Control-Max-Age: 预检有效期,单位为秒。有效期内,不需要重复发送预检请求
• Access-Control-Expose-Headers: Header 白名单,不设置的话,客户端读不到 Header 的内容
二、解决办法
第一方法:
import org.springframework.context.annotation.Configuration;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter(filterName = "CorsFilter ")
@Configuration
public class CorsFilter implements Filter {
@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-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PATCH, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
chain.doFilter(req, res);
}
}
这种办法,是基于过滤器的方式,方式简单明了,这种办法,是基于过滤器的方式,方式简单明了,就是把这些响应头写入到response响应中去
第二种方法:
public class GoodsController {
@CrossOrigin(origins = "http://localhost:4000")
@GetMapping("goods-url")
public Response queryGoodsWithGoodsUrl(@RequestParam String goodsUrl) throws Exception {
}
}
这种方法就是采用@CrossOrigin注解,查看@CrossOrigin源码,就能知道这注解可以放到method、class等上面,类似于@RequestMapping,也就是说这个这个注解可以控制class类中的所有方法跨域,也可以让单个方法进行跨域。这种方式虽然实现比较简单,但此方法只能实现局部跨域,当一个项目中存在多个类或者方法的话,使用这种方法比较麻烦,需要给所有类或者方法都添加上此注解。
第三种方法:
@Configuration
public class CrossDomainStarter {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
//放行的域名
config.addAllowedOrigin("*");
//放行的请求类型,有 GET, POST, PUT, DELETE, OPTIONS
config.addAllowedMethod("*");
//放行的请求头
config.addAllowedHeader("*");
//暴露头部信息
config.addExposedHeader("*");
//是否允许发送 Cookie
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
corsConfigurationSource.registerCorsConfiguration("/**",config);
return new CorsFilter(corsConfigurationSource);
}
}
而 Spring-Framework 5.3 版本之后,关于 CORS 跨域配置类 CorsConfiguration 中将 addAllowedOrigin 方法名修改为 addAllowedOriginPattern,因此配置了变成了以下这样:
@Configuration
public class CrossDomainStarter {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
//放行的域名
config.addAllowedOriginPattern("*");
//放行的请求类型,有 GET, POST, PUT, DELETE, OPTIONS
config.addAllowedMethod("*");
//放行的请求头
config.addAllowedHeader("*");
//暴露头部信息
config.addExposedHeader("*");
//是否允许发送 Cookie
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
corsConfigurationSource.registerCorsConfiguration("/**",config);
return new CorsFilter(corsConfigurationSource);
}
}
区别:
- addAllowedOriginPattern:这个方法允许通过使用通配符模式来匹配多个来源。您可以使用正则表达式或简单的通配符模式来指定允许的来源。例如,您可以使用addAllowedOriginPattern("*")来允许所有来源的访问,或使用addAllowedOriginPattern("https://*.example.com")来允许以"https://"开头并以".example.com"结尾的任何来源访问。这种方式更加灵活,可以满足多种来源的需求。
- addAllowedOrigin:这个方法用于指定允许访问资源的具体来源。您需要明确指定每个允许的来源。例如,您可以使用addAllowedOrigin("https://example.com")来允许只有"http://example.com"这个来源访问资源。这种方式更加具体和明确
Spring Security CORS 处理
Spring Boot 环境下如果引入了 Spring Security,Spring 将自动配置 CorsFilter,此时从CorsConfigurationSource 类型的 bean 中读取 CORS 配置,因此将 CorsConfigurationSource 配置为 bean 即可。示例代码如下。
@Configuration
public class WebMvcConfig {
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.setExposedHeaders(Arrays.asList("header1", "header2"));
corsConfiguration.setMaxAge(3600L);
corsConfiguration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
corsConfigurationSource.registerCorsConfiguration("/*", corsConfiguration);
return corsConfigurationSource;
}
}
Nginx 跨域处理
在Nginx服务器的配置文件中添加以下代码:
server {
listen 80;
server_name your_domain.com;
location /api {
# 允许跨域请求的域名,* 表示允许所有域名访问
add_header 'Access-Control-Allow-Origin' '*';
# 允许跨域请求的方法
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
# 允许跨域请求的自定义 Header
add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept';
# 允许跨域请求的 Credential
add_header 'Access-Control-Allow-Credentials' 'true';
# 预检请求的存活时间,即 Options 请求的响应缓存时间
add_header 'Access-Control-Max-Age' 3600;
# 处理预检请求
if ($request_method = 'OPTIONS') {
return 204;
}
}
# 其他配置...
}
上述示例中,location /api 代表配置针对 /api 路径的请求进行跨域设置
小结
跨域问题可以在网关层、反向代理层或应用层来解决,而它们的使用优先级是:网关层 > 代理层 > 应用层。因为越靠前覆盖范围就越大,解决跨域问题就越容易