跨域问题解决方案(SpringBoot)

什么是跨域访问

说到跨域访问,必须先解释一个名词:同源策略。所谓同源策略就是在浏览器端出于安全考量,向服务端发起请求必须满足:协议相同、Host(ip)相同、端口相同的条件,否则访问将被禁止,该访问也就被称为跨域访问。

虽然跨域访问被禁止之后,可以在一定程度上提高了应用的安全性,但也为开发带来了一定的麻烦。比如:我们开发一个前后端分离的项目,页面及js部署在一个主机的nginx服务中,后端接口部署在一个tomcat应用容器中,当前端向后端发起请求的时候一定是不符合同源策略的,也就无法访问。那么我们如何解决这个问题?就是本文需要向大家说明的内容。

解决方案:CORS

CORS简介

CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing)。它允许浏览器向跨源(协议 + 域名 + 端口)服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。CORS需要浏览器和服务器同时支持。它的通信过程,都是浏览器自动完成,不需要用户参与。

对于开发者来说,CORS通信与同源的AJAX/Fetch通信没有差别,代码完全一样。浏览器一旦发现请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。

浏览器发出CORS简单请求,只需要在头信息之中增加一个Origin字段。

浏览器发出CORS非简单请求,会在正式通信之前,增加一次OPTIONS查询请求,称为”预检”请求(preflight)。浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

简单请求就是HEAD、GET、POST请求,并且HTTP的头信息不超出以下几种字段 Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type

注:Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

反之,就是非简单请求。

第一种方案

自定义一个配置类继承自WebMvcConfigurer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CrossConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")//添加映射路径,“/**”表示对所有的路径实行全局跨域访问权限的设置
.allowedOriginPatterns("*")//开放哪些ip、端口、域名的访问权限
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")//开放哪些Http方法,允许跨域访问
.allowCredentials(true)//是否允许发送Cookie信息
.maxAge(3600)//预请求缓存时间,单位秒,默认为30分钟
.allowedHeaders("*");//允许HTTP请求中的携带哪些Header信息
}
}

第二种解决方案

在controller类上使用@CrossOrigin注解

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
@RequestMapping("/jdbc/user")
public class UserController {
@Resource
private UserMapper userMapper;

@CrossOrigin
@RequestMapping("findAll")
public List<User> findAll() {
return userMapper.selectList(null);
}
}

当然也可在整个controller类上添加注解,这样controller内的所有请求都能被访问到

查看@CrossOrigin注解

1
2
3
4
5
6
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin {
...
}

从元注解@Target可以看出,注解可以放在method、class等上面,类似RequestMapping,也就是说,整个controller下面的方法可以都受控制,也可以让单个方法受控制。

也可以得知,这个是最小粒度的CORS控制办法了,精确到单个请求级别。