跨域请求的概念

浏览器出于安全的考虑,使用 XMLHttpRequest对象发起 HTTP请求时必须遵守同源策略,否则就是跨域的HTTP请求,默认情况下是被禁止

同源策略要求源相同才能正常进行通信,即协议、域名【或者是主机名】、端口号都完全一致。

说通俗一点

  • 浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域
  • 只要协议、域名、端口有任何一个不同,都被当作是不同的域,之间的请求就是跨域操作。

例如:

前端 后端
http://localost:8080 http://localhost:8081

协议相同,主机名相同,但端口号不同。这就是跨域请求。

注意

  • 只有浏览器才会有这个跨域请求的问题,如果是移动端跨域请求就不会出现问题。
  • XMLHttpRequest对象就是ajax发送异步请求时使用的对象,所以也就是说浏览器使用ajax请求时才会出现跨域请求的问题。

总得来说,发生跨域请求问题的场景就是:浏览器使用ajax请求

解决方案

解决跨域请求有多种方案

我们可以使用CORS解决跨域。

什么是CORS

CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing),允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

​ 它通过服务器增加一个特殊的Header[Access-Control-Allow-Origin]来告诉客户端跨域的限制,如果浏览器支持CORS、并且判断Origin通过的话,就会允许XMLHttpRequest发起跨域请求。

如果是一个跨域请求,就会再发送一个带有Origin的请求头。里面的值就是这个页面所在的域

image-20220825170142608

发送给服务器之后,如果我们配置了跨域,响应头就会带有Access-Control-Allow-Origin,它的值表示服务器允许跨域的域

如果该值和请求头的Origin值一样,则表示允许跨域。就会请求成功。

image-20220825170405785

如果我们要自己使用Cors解决跨域请求的问题其实就是

解析请求头,然后去添加响应头就好了。但我们没必要自己去写,SpringBoot有这样的解决跨域请求的拦截器,只需要配置就可以。

如果我们进行了Cors配置,但响应头中没有Access-Control-Allow-Origin,说明我们的配置是有问题的。

SpringBoot使用CORS解决跨域

  • 使用@CrossOrigin

    可以在支持跨域的方法上或者是Controller类上加上@CrossOrigin注解

    例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @RestController
    @RequestMapping("/user")
    @CrossOrigin
    public class UserController {

    @Autowired
    private UserServcie userServcie;

    @RequestMapping("/findAll")
    public ResponseResult findAll(){
    //调用service查询数据 ,进行返回
    List<User> users = userServcie.findAll();

    return new ResponseResult(200,users);
    }
    }
    • value属性可以设置多个URL。

    • origins属性也可以设置多个URL。例如:@CrossOrigin(origins = "http://domain2.com", maxAge = 3600)

    • maxAge属性指定了准备响应前的缓存持续的最大时间。就是探测请求的有效期。

    • allowCredentials属性表示用户是否可以发送、处理 cookie。默认为false

    • allowedHeaders 属性表示允许的请求头部有哪些。

    • methods 属性表示允许请求的方法,默认get,post,head

    如果你不设置他的value属性,或者是origins属性,就默认是可以允许所有的URL/域访问。

    value和origins的效果是一样的,当value和origins同时使用时,配置的值不一样会报错!

  • 使用 WebMvcConfigureraddCorsMappings 方法配置CorsInterceptor

    使用注解有一个缺陷,当我们的Controller类过多时,就需要频繁加注解。因此我们可以使用配置类才进行批量映射

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @Configuration
    public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
    // 设置允许跨域的路径
    registry.addMapping("/**")
    // 设置允许跨域请求的域名
    .allowedOriginPatterns("*")
    // 是否允许cookie
    .allowCredentials(true)
    // 设置允许的请求方式
    .allowedMethods("GET", "POST", "DELETE", "PUT")
    // 设置允许的header属性
    .allowedHeaders("*")
    // 跨域允许时间
    .maxAge(3600);
    }
    }
    • addMapping("/user/**"):其中* 表示匹配到下一层;** 表示后面不管有多少层,都能匹配。

    • allowedOriginPatterns("http:localhost:8080"),只有http:localhost:8080可以访问。*表示任意域名

    • .maxAge(3600);

      如果我们使用比较复杂的请求,比如PUT请求。

      那这个请求它不是只发送一个请求,如果是跨域请求,浏览器会先发送一个询问允许我跨域请求的请求。如果服务端是允许的,就会再响应头中添加对应的响应头。

      如果每次请求都发送一个询问请求那就会很浪费时间。

      maxAge相当于持久的时间。如果我们这个请求被允许了,那在3600秒之内,浏览器再去发送跨域请求,就不会再发送询问请求。

当时使用SpringSecurity时

当有跨域请求时,还得经过SringSecurity的过滤器,光靠SpringBoot是不行的,如果SpringSecurity是不允许跨域的还是不行。

因此还需要配置SpringSecurity来进行跨域请求。配置方法如下:http.cors();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter{

@Override
protected void configure(HttpSecurity http) throws Exception {

http
//关闭csrf
.csrf().disable()
//不通过Session获取SecurityContext
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// 对于登录接口 允许匿名访问
.antMatchers("/user/login").anonymous()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated();

http.cors();//允许跨域请求

}
}

__END__