一個CORS請求的流程
-
發(fā)送一個預檢請求
都為Options請求,因為Options請求不會對服務器做出任何改動。且?guī)в?strong>"contentType:"application/json"請求頭的get或者post才會進行預檢請求。
-
檢查
- 服務器驗證該請求的origin是否在Access-Control-Allow-Origin范圍內
- 請求方法是否在Access-Control-Allow-Methods范圍內
- 請求頭是否都在Access-Control-Allow-Headers范圍內
-
如果都符合了,瀏覽器將繼續(xù)執(zhí)行ajax請求,否則瀏覽器的控制臺則打印以下信息:
img
如何開啟并成功請求
-
只需要在服務端開啟即可??蛻舳藷o需任何改動
-
NodeJs版:
var app = express(); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({extended: false})); app.use(function (req, res, next) { res.header("Access-Control-Allow-Origin", "http://localhost:3000"); res.header("Access-Control-Allow-Methods", "PUT, GET, POST, DELETE, OPTIONS"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization, Access-Control-Allow-Credentials"); res.header("Access-Control-Allow-Credentials", "true"); next(); }); -
java版(spring-boot)
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; @Configuration public class CorsConfig { private CorsConfiguration buildConfig() { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.addAllowedOrigin("*"); corsConfiguration.addAllowedHeader("*"); corsConfiguration.setMaxAge(86400L); corsConfiguration.addAllowedMethod(HttpMethod.GET); corsConfiguration.addAllowedMethod(HttpMethod.POST); corsConfiguration.addAllowedMethod(HttpMethod.PUT); corsConfiguration.addAllowedMethod(HttpMethod.DELETE); return corsConfiguration; } @Bean public FilterRegistrationBean corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", buildConfig()); FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source)); return bean; } } -
java-spring-mvc版
import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Arrays; import java.util.List; /** * @author shihu */ public class CorsFilter extends OncePerRequestFilter { //可以自行設置允許跨域的域名?;蛘?,代表允許所有 private final static String[] allowOrigin = new String[]{"localhost:3000","localhost:3001"}; @Override public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException { String currentOrigin = request.getHeader("Origin"); List<String> allowOriginList = Arrays .asList(allowOrigin); if (!allowOriginList.isEmpty()) { if (allowOriginList.contains("*") || allowOriginList.contains(currentOrigin)) { response.setHeader("Access-Control-Allow-Origin",currentOrigin); } } response.setHeader("Access-Control-Max-Age", "86400");//設置再次發(fā)起預檢請求的過期時間 response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS"); response.setHeader("Access-Control-Allow-Credentials","true"); response.setHeader("Access-Control-Allow-Headers", "*"); filterChain.doFilter(request, response); } @Override public void destroy() { } }
-
注意事項
-
有人想每次發(fā)起預檢Options請求會覺得浪費資源。后端可以設置Access-Control-Max-Age,就允許在這個時間段內,客戶端可以不用再次發(fā)起預檢請求。
response.setHeader("Access-Control-Max-Age", "86400"); -
為什么能夠發(fā)起請求,并且成功得到了響應,響應也有內容,但是瀏覽器不執(zhí)行請求的響應的腳本。
如:發(fā)起一個get請求。得到了服務器的響應。

但是瀏覽器控制臺沒有繼續(xù)執(zhí)行。并報一下錯誤。因為沒有帶contentType:"application/json請求頭的get請求不會發(fā)起預檢請求。瀏覽器無法得知是否允許。但是當響應了,發(fā)起請求的域名沒有在允許跨域的列表內,瀏覽器就會為了安全,不會繼續(xù)執(zhí)行請求之后的腳本

例子
我寫了一個例子,在線地址:http://111.230.165.16:3003/index
源碼:https://github.com/shihua-guo/Java-Learn/tree/master/CORS-Test。
包含了很多種情況的跨域請求。大家能夠非常直觀的觀察跨域請求是如何進行的:


總結
因為之前一直都是復制別人的代碼來處理跨域的,自己沒有去了解過。而且大部分都是通過代理解決跨域的。這次就寫的詳細一點,以后就不會忘記了
其實很簡單,就是在服務端設置了允許跨域的域(Access-Control-Allow-Origin)、設置了允許跨域的方法(Access-Control-Allow-Methods)、設置了允許跨域的請求頭(Access-Control-Allow-Headers)就可以解決跨域共享的問題了。
