Spring MVC 跨域

本文最后更新于:2024年9月8日 晚上

Spring MVC 跨域

  • 跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。
  • 广义的跨域。
    • 资源跳转:A链接,重定向,表单提交。
    • 资源嵌入:<link>,<script>,<img>,<frame>等dom标签,还有样式中background:url(),@font-face()等文件外链。
    • 脚本请求:js发起的ajax请求,dom和js对象的跨域操作等。
  • 其实我们通常所说的跨域是狭义的,是由浏览器同源策略限制的一类请求场景。

同源策略

  • 同源策略/SOP(Same origin policy)是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS,CSFR等攻击,所谓同源是指协议+域名+端口三者相同,即便两个不同的域名指向同一个ip地址,也非同源。
  • 同源策略限制以下几种行为:
    • Cookie,LocalStorage 和 IndexDB 无法读取。
    • DOM 和 JS对象无法获得。
    • AJAX 请求不能发送。

常见跨域场景

URL 说明 是否允许通信
http://www.domain.com/a.js
http://www.domain.com/b.js
http://www.domain.com/lab/c.js
同一域名,不同文件或路径 允许
http://www.domain.com:8000/a.js
http://www.domain.com/b.js
同一域名,不同端口 不允许
http://www.domain.com/a.js
https://www.domain.com/b.js
同一域名,不同协议 不允许
http://www.domain.com/a.js
http://192.168.4.12/b.js
域名和域名对应相同ip 不允许
http://www.domain.com/a.js
http://x.domain.com/b.js
http://domain.com/c.js
主域相同,子域不同 不允许
http://www.domain1.com/a.js
http://www.domain2.com/b.js
不同域名 不允许

跨域资源共享(CORS)

前端设置

jQuery ajax

1
2
3
4
5
6
7
8
$.ajax({
...
xhrFields: {
withCredentials: true // 前端设置是否带cookie
},
crossDomain: true, // 会让请求头中包含跨域的额外信息,但不会含cookie
...
});

Vue axios

  • axios设置。
1
axios.defaults.withCredentials = true
  • vue-resource设置。
1
Vue.http.options.credentials = true
  • 注意:如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确与请求网页一致的域名,同时,Cookie依然遵循同源策略,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie

服务端设置

  • 若后端设置成功,前端浏览器控制台则不会出现跨域报错信息,反之,说明没设成功。

使用Filter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Component
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-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
chain.doFilter(req, res);
}
@Override
public void init(FilterConfig filterConfig) {}
@Override
public void destroy() {}
}

使用WebMvcConfigurer

1
2
3
4
5
6
7
8
9
10
11
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "DELETE", "PUT","PATCH")
.maxAge(3600);
}
}

使用CorsConfigurationSource

  • 引入Spring Security 后需要使用该配置方式,登陆与登出路径由Spring Security直接接管,并不受Spring MVC 的跨域配置。
1
2
3
4
5
6
7
8
9
10
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Collections.singletonList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
configuration.applyPermitDefaultValues();
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}

nginx代理跨域

nginx配置解决iconfont跨域

  • 浏览器跨域访问js,css,img等常规静态资源被同源策略许可,但iconfont字体文件(eot|otf|ttf|woff|svg)例外,此时可在nginx的静态资源服务器中加入以下配置
1
2
3
location / {
add_header Access-Control-Allow-Origin *;
}

nginx反向代理接口跨域

  • 跨域原理:同源策略是浏览器的安全策略,不是HTTP协议的一部分,服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。
  • 实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#proxy服务器
server {
listen 80;
server_name www.domain1.com;

location / {
proxy_pass http://www.domain2.com:8080; #反向代理。
proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名。
index index.html index.htm;

# 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用。
add_header Access-Control-Allow-Origin http://www.domain1.com; #当前端只跨域不带cookie时,可为*
add_header Access-Control-Allow-Credentials true;
}
}