赞
踩
什么是跨域,首先可以参考我之前写的这篇文章:JavaWeb跨域问题及解决方案 ,另外我下面会做补充。
很多人误认为资源跨域时无法请求,实际上,通常情况下请求是可以正常发起的(注意,部分浏览器存在特例),后端也正常进行了处理,只是在返回时被浏览器拦截,导致响应内容不可使用。此外,我们平常所说的跨域实际上都是在讨论浏览器行为。
CORS(Cross-Origin Resource Sharing)的规范中有一组新增的HTTP首部字段,允许服务器声明其提供的资源允许哪些站点跨域使用。通常情况下,跨域请求即便在不被支持的情况下,服务器也会接收并进行处理,在CORS的规范中则避免了这个问题。浏览器首先会发起一个请求方法为OPTIONS 的预检请求,用于确认服务器是否允许跨域,只有在得到许可后才会发出实际请求。此外,预检请求还允许服务器通知浏览器跨域携带身份凭证(如cookie)。
CORS常见首部字段:
简单请求:
预检请求:
带凭证的请求:
下面介绍Spring Boot三种跨域解决方案:
Spring MVC的请求顺序一般是:浏览器->filter->DispatcherServlet->interceptor->controller,因此用CorsFilter会更早地去处理跨域,效果也更好。
配置CorsFilter如下代码所示。
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; import java.util.Arrays; @Configuration public class CrossoriginConfig { @Bean public FilterRegistrationBean<CorsFilter> corsFilter(){ FilterRegistrationBean<CorsFilter> registrationBean = new FilterRegistrationBean<>(); CorsConfiguration corsConfig = new CorsConfiguration(); // cors配置 // 后端服务器(http://localhost:8182)为资源提供方,AllowedOrigins可配置哪些网站可以获取我的资源 corsConfig.setAllowedOrigins(Arrays.asList("http://localhost")); //corsConfig.setAllowedOrigins(Arrays.asList("*")); // 预检请求的响应中有效 corsConfig.setAllowedMethods(Arrays.asList("GET","POST")); corsConfig.setAllowedHeaders(Arrays.asList("Content-Type")); //corsConfig.setMaxAge(Duration.ofHours(1L)); corsConfig.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**",corsConfig); registrationBean.setFilter(new CorsFilter(source)); registrationBean.setOrder(-1); return registrationBean; } }
import org.springframework.web.bind.annotation.*; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; @RestController @RequestMapping("/user") public class UserController { @GetMapping("/getName") public String getName(){ return "用户的名称为张三"; } @PostMapping("/save") public String save(@RequestBody Map<String,String> user, HttpServletRequest request, HttpServletResponse response){ SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); System.out.println("username:"+user.get("username")); System.out.println("password:"+user.get("password")); // 测试跨域携带cookie boolean hasMyCookie = false; if(null != request.getCookies() && request.getCookies().length > 0){ for (Cookie cookie : request.getCookies()) { if("sb-co".equals(cookie.getName())){ hasMyCookie = true; } System.out.println(cookie.getName()+":"+cookie.getValue()); } } if(!hasMyCookie){ Cookie cookie = new Cookie("sb-co", format.format(new Date())); cookie.setMaxAge(3600); response.addCookie(cookie); } return "保存用户成功"; } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="jquery-3.4.1.js"></script> <script> $(function () { $("#getName").click(function () { $.ajax({ url: "http://localhost:8182/user/getName", method : "get", success:function (result) { alert(result) } }); }); $("#saveUser").click(function () { $.ajax({ url: "http://localhost:8182/user/save", method : "post", contentType : "application/json;charset=UTF-8", data : JSON.stringify({ "username":"孙悟空" }), xhrFields : {withCredentials : true}, success:function (result) { alert(result) } }); }); }); </script> </head> <body> <button id="getName">简单请求-获取用户名称</button> <button id="saveUser">预检请求:保存用户</button> </body> </html>
将静态资源文件放到某个目录下,然后在nginx.conf中配置location,如下所示。
# server模块下;root为静态资源存放目录
server {
listen 80;
location / {
root html/SpringMvc;
index index.html index.htm;
}
}
然后启动nginx,作为静态资源服务器。
启动Spring Boot。
浏览器访问http://localhost ,如下图所示。
分别测试简单请求和预检请求,观察浏览器控制台Network,如下图所示。
preflight就是预检请求。状态都是200,说明跨域是OK的。
通过UserController.save方法的代码可知,第一次请求时会response会携带cookie “sb-co”,这是一个跨域cookie。当我们再次请求时,由于我们配置了允许跨域cookie,请求里会携带“sb-co”,后端控制台也能打印该cookie的值,如下图所示。
引入spring security框架后,第1、2种方案都会失效,第3种方案如果过滤器的优先级要低于spring security的优先级也会失效。
如果配置的是第1、2种方案或低优先级filter的第3种方案,预检请求会先到达spring security的过滤器,由于预检请求不会携带任何凭证信息,因此会被拦截下来。
spring security解决方案:
以下有三种方式可以开启spring security的cors:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。