赞
踩
对于前后端分离的项目来说,如果前端项目与后端项目部署在两个不同的域下,那么势必会引起跨域问题的出现。
跨域指的是从一个域名去请求另外一个域名的资源。即跨域名请求,跨域时,浏览器不能执行其他域名网站的脚本,是由浏览器的 “同源策略” 造成的,是浏览器施加的安全限制。跨域的严格一点来说就是只要协议,域名,端口有任何一个的不同,就被当作是跨域。
下面举个例子:
判断下面 URL 是否和 http://www.baidu.com/a/a.html 同源
综上所述,在同源策略的限制下,非同源的网站之间不能发送 AJAX 请求。如有需要,可通过降域或其他技术实现。
下面实现一个案例来验证一下
package com.example.demo.controller; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("server") public class TestController { @RequestMapping("test") public String test(HttpServletResponse httpResponse,HttpSession session){ return "阿三"; } }
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>test</title> </head> <body> <script type="text/javascript" src="https://code.jquery.com/jquery-3.1.1.min.js"></script> <div id="msg"> </div> <script type="text/javascript"> $(document).ready(function(){ $.ajax({ url:'http://localhost:8090/server/test', type:'get', dataType:'json', success:function(res){ console.log(res); $("#msg").html(res.msg); }, error:function(){ console.log("error"); } }) }); </script> </body> </html>
由于两项目的端口不通,所以造成了跨域访问。
那么如何解决跨域问题?,跨域问题解决方案有很多,本文主要讲通过 CORS 协议解决跨域问题。
CORS 是一个 W3C 标准,全称是 "跨域资源共享”(Cross-origin resource sharing)。它允许浏览器向跨源(协议 + 域名 + 端口)服务器,发出 XMLHttpRequest 请求,从而克服了 AJAX 只能同源使用的限制。CORS 需要浏览器和服务器同时支持。它的通信过程,都是浏览器自动完成,不需要用户参与。
对于开发者来说,CORS 通信与同源的 AJAX/Fetch 通信没有差别,代码完全一样。浏览器一旦发现请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,就可以跨源通信。
浏览器将 CORS 请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
在 CORS 出现前,发送 HTTP 请求时在头信息中不能包含任何自定义字段,且 HTTP 头信息不超过以下几个字段:
Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type(值限于 3 个值:application/x-www-form-urlencoded、multipart/form-data、text/plain)
这里给出一个例子:
GET /baidu HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, sdch, br
Origin: http://www.baidu.com
Host: www.baidu.com
对于简单请求,CORS 的策略是请求时在请求头中增加一个 Origin 字段,服务器收到请求后,根据该字段判断是否允许该请求访问。
对于非简单请求的跨源请求,浏览器会在真实请求发出前,增加一次 OPTION 请求,称为预检请求 (preflight request) 。预检请求将真实请求的信息,包括请求方法、自定义头字段、源信息添加到 HTTP 头信息字段中,询问服务器是否允许这样的操作。
下面的请求会触发预检请求:
以下是一个发起预检请求的例子:
发起请求的 origin 与请求的服务器的 host 不同,而且根据上面的条件判断,触发了预检
这里说一下这几个的含义:
当预检请求通过后,浏览器才会发送真实请求到服务器。这样就实现了跨域资源的请求访问。
spring mvc 4.2 版本增加了对 CORS 的支持,通过 Spring Boot 可以非常简单的实现跨域访问。
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 CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
.allowCredentials(true)
.maxAge(3600)
.allowedHeaders("*");
}
这种方式是全局配置的,但是很多都是基于旧的 Spring 版本,比如 WebMvcConfigurerAdapter 在 Spring5.0 已经被标记为 Deprecated,我们通过看源码就可以知道:
/**
* An implementation of {@link WebMvcConfigurer} with empty methods allowing
* subclasses to override only the methods they're interested in.
*
* @author Rossen Stoyanchev
* @since 3.1
* @deprecated as of 5.0 {@link WebMvcConfigurer} has default methods (made
* possible by a Java 8 baseline) and can be implemented directly without the
* need for this adapter
*/
@Deprecated
public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {}
通过实现 Fiter 接口在请求中添加一些 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); }
在 Controller 上使用 @CrossOrigin 注解,该类下的所有接口都可以通过跨域访问。
@RestController
@RequestMapping("/test")
//@CrossOrigin //所有域名均可访问该类下所有接口
@CrossOrigin("https://www.baidu.com") // 只有指定域名可以访问该类下所有接口
public class CorsTestController {
@GetMapping("/test2")
public String sayHello() {
return "hello world";
}
}
这里指定当前的 CorsTestController 中所有的方法可以处理 https://www.baidu.com 域上的请求。
我们点击 CrossOrigin 进去源码看一下:
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin {
}
从注解 @Target 可以看出,注解可以放在 method、class 等上面,类似 RequestMapping,也就是说,整个Controller 下面的方法可以都受控制,也可以单个方法受控制。
这里不管是通过哪种方式配置 CORS,都是在构造 CorsConfiguration,一个 CORS 配置用一个 CorsConfiguration类来表示,如下所示:
Spring 中对 CORS 规则的校验,通过委托给 DefaultCorsProcessor实现。
处理流程如下:
校验就是根据 CorsConfiguration 这个类的配置进行判断:
本文主要介绍了 CORS 的知识,以及如何在 Spring Boot 中配置 CORS。希望对小伙伴的学习有所帮助。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。