跨域问题在前后端分离的开发场景中经常遇到,回想起来自己也已经折腾了数次,本篇文章主要对跨域问题做个记录和总结。
跨域在前端中的报错一般为:
代码语言:javascript复制Access to XMLHttpRequest at *** from origin *** has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the request resource.
一、为什么会出现跨域问题
任何一个系统都有着或多或少的规则和限制。跨域问题的来源便是浏览器的同源策略:
同源策略是指:协议相同、域名相同、端口相同。
为什么要有同源策略呢?
试想你登录一家电商网站,该网站肯定会在本地存储一些你的隐私信息,这个时候你又登录另一个网站,如果没有同源策略,该网站就可以直接读取到你在电商网站的隐私信息,这对用户来说是完全不可接受的。
也就说,只要违反了同源策略的任何一种,都是跨域。
现代浏览器给出了限制非同源的三种行为:
- Cookie、LocalStorage 和 IndexedDB,非同源,不可读写。
- 网页资源,比如DOM,非同源,不可接触。
- 发送AJAX请求,非同源,浏览器拒绝响应
二、跨域解决方案
跨域的解决方案有很多种:
一 . 使用Ajax的jsonp
代码语言:javascript复制$.ajax({
url: "http://localhost:8090/crossorigin_test/getList",
dataType: 'jsonp',
success: function (data) {
}
})
JSONP 只支持get请求、不支持post请求
二. CORS解决跨域
跨源资源共享 (CORS)是一种基于 HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其它origin(域,协议和端口),这样浏览器可以访问加载这些资源。
CORS整个通信过程都由浏览器自动完成,CORS通信与同源的AJAX请求代码逻辑完全一样,只要服务器实现了CORS接口,浏览器就会自动携带一些附加的请求头信息,从而实现跨源通信,对用户而言是无感知的。
CORS需要浏览器和服务器的支持,CORS已经被现代浏览器广泛采用,因此服务器端的支持是关键。
我们重点看看后端使用Spring如何配置CORS跨域
1. Servlets方式手工设置响应头
创建跨域拦截器实现HandlerInterceptor接口,并实现其方法,在请求处理前设置头信息,并放行
代码语言:javascript复制public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 设置:Access-Control-Allow-Origin头,处理Session问题
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("P3P", "CP=CAO PSA OUR");
if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) {
response.addHeader("Access-Control-Allow-Methods", "POST,GET,TRACE,OPTIONS");
response.addHeader("Access-Control-Allow-Headers", "Content-Type,Origin,Accept");
response.addHeader("Access-Control-Max-Age", "120");
}
// 放行
return ture;
}
再在配置文件中配置拦截器
代码语言:javascript复制<mvc:interceptos>
<mvc:interceptor>
<!--拦截所有-->
<mvc:mapping path="/*/**"/>
<bean class="com.datahear.CrossOriginInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
2. 使用注解 @CrossOrigin
在类上加注解,表示类下所有方法都支持跨域请求
代码语言:javascript复制@CrossOrigin
@RestController
@RequestMapping("users")
public class AaaController {
}
在方法上加注解,表示该方法支持跨域请求
代码语言:javascript复制@RestController
@RequestMapping("users")
public class AaaController {
@CrossOrigin
@RequestMapping("/getUser")
public Result getUser(HttpServletRequest request, HttpServletResponse response) throws Exception {
……
}
}
3. 实现WebMvcConfigurer接口,重写addCorsMappings方法
代码语言:javascript复制@Configuration
public class MvcConfig implements WebMvcConfigurer {
/**
* 解决跨域请求
* @return
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowCredentials(true)
.allowedOrigins("*")
.allowedHeaders("*")
.allowedMethods("*")
.maxAge(3600);
WebMvcConfigurer.super.addCorsMappings(registry);
}
}
4. 使用CorsFilter过滤器
代码语言:javascript复制import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class CorsConfig {
// 当前跨域请求最大有效时长。这里默认30天
private long maxAge = 30 * 24 * 60 * 60;
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*"); // 1 设置访问源地址
corsConfiguration.addAllowedHeader("*"); // 2 设置访问源请求头
corsConfiguration.addAllowedMethod("*"); // 3 设置访问源请求方法
corsConfiguration.setMaxAge(maxAge);
corsConfiguration.setAllowCredentials(true);
return corsConfiguration;
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig()); // 4 对接口配置跨域设置
return new CorsFilter(source);
}
}
三. Nginx反向代理
利用nginx反向代理把跨域问题转为不跨域,支持各种请求方式
直接看nginx配置:
代码语言:javascript复制location ^~/wx
{
proxy_pass http://localhost:8082;
}
代码语言:javascript复制location /admin/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://localhost:8080/;
}