起因
当我们前后端分离时,前端发送请求就非常有可能出现跨域问题。
此时浏览器在本地的5500端口运行,此时若是发送给本地的9999就会出现跨域问题!
描述:浏览器中有同源策略,其指的是发送请求的协议、域名以及端口必须与向后端的请求一致,否则就会跨域问题。想要解决跨域问题,就需要后端添加响应头Access-Control-Allow-Origin属性。
Springboot解决方案
1、添加@CrossOrigin
注解源码
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CrossOrigin { @Deprecated String[] DEFAULT_ORIGINS = {"*"}; @Deprecated String[] DEFAULT_ALLOWED_HEADERS = {"*"}; @Deprecated boolean DEFAULT_ALLOW_CREDENTIALS = false; @Deprecated long DEFAULT_MAX_AGE = 1800; String[] value() default {}; @AliasFor("value") String[] origins() default {}; String[] originPatterns() default {}; String[] allowedHeaders() default {}; String[] exposedHeaders() default {}; RequestMethod[] methods() default {}; String allowCredentials() default ""; long maxAge() default -1; }
String[] origins: 允许来源域名的列表,例如 'www.jd.com',匹配的域名是跨域预请求 Response 头中的 'Access-Control-Aloow_origin' 字段值。不设置确切值时默认支持所有域名跨域访问。 String[] allowedHeaders: 跨域请求中允许的请求头中的字段类型, 该值对应跨域预请求 Response 头中的 'Access-Control-Allow-Headers' 字段值。 不设置确切值默认支持所有的header字段(Cache-Controller、Content-Language、Content-Type、Expires、Last-Modified、Pragma)跨域访问。 String[] exposedHeaders: 跨域请求请求头中允许携带的除Cache-Controller、Content-Language、Content-Type、Expires、Last-Modified、Pragma这六个基本字段之外的其他字段信息,对应的是跨域请求 Response 头中的 'Access-control-Expose-Headers'字段值。 RequestMethod[] methods: 跨域HTTP请求中支持的HTTP请求类型(GET、POST...),不指定确切值时默认与 Controller 方法中的 methods 字段保持一致。 String allowCredentials: 该值对应的是是跨域请求 Response 头中的 'Access-Control-Allow-Credentials' 字段值。浏览器是否将本域名下的 cookie 信息携带至跨域服务器中。默认携带至跨域服务器中,但要实现 cookie 共享还需要前端在 AJAX 请求中打开 withCredentials 属性。 long maxAge: 该值对应的是是跨域请求 Response 头中的 'Access-Control-Max-Age' 字段值,表示预检请求响应的缓存持续的最大时间,目的是减少浏览器预检请求/响应交互的数量。默认值1800s。设置了该值后,浏览器将在设置值的时间段内对该跨域请求不再发起预请求。
使用方式:
//方式一:在类上(表明该类所有方法都能够支持跨域) @CrossOrigin class public class UserController {} //方式二:细粒度在方法上(表明该方法支持跨域) @RestController class public class UserController { @RequestMapping("/user") @CrossOrigin public String query(){ ... } }
2、全局CORS配置(实现WebMvcConfigurer的addCorsMappings方法)
定义全局CORS配置,来对指定模块进行跨域支持,类似于筛选器,我们在这里可以进行重写方法来实现针对指定映射地址来进行设置跨域:
@Configuration public class MyWebMvcConfig implements WebMvcConfigurer { /** * 解决跨域问题 */ @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/user/**") //指定的映射地址 .allowedHeaders("*") //允许携带的请求头 .allowedMethods("*") //允许的请求方法 .allowedOrigins("*"); //添加跨域请求头 Access-Control-Allow-Origin,值如:"https://domain1.com"或"*" } }
3、基于filter的跨域实现(筛选白名单来进行跨域请求)
好处:基于这种方式的话我们能够对指定的url进行筛选来达到允许进行跨域请求的方式!!!
/** * @author ChangLu * @date 2021/08/30 11:29 **/ @Component public class CorsFilter extends OncePerRequestFilter { private static List<String> whiteList = new ArrayList<>();//跨域白名单 static { whiteList.add("http://127.0.0.1:5500"); // whiteList.add("http://jshopx.jd.com"); // whiteList.add("http://mall.yao.jd.com"); // whiteList.add("http://yao-shop.jd.com"); } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { //请求的地址 String originUrl = request.getHeader("origin"); //查看是否在白名单内 boolean allow = whiteList.contains(originUrl); if(allow){ response.setHeader("Access-Control-Allow-Origin", originUrl); response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS"); response.setHeader("Access-Control-Max-Age", "1800");//30分钟 response.setHeader("Access-Control-Allow-Headers", "x-requested-with, content-type"); filterChain.doFilter(request, response); }else{ //对于非白名单域名的请求,不予进行访问,不然还会进入到controller方法中执行对应的逻辑 return; } } }
OncePerRequestFilter实际上则是实现了Filter,本质就是一个Filter过滤器,只不过这个也如同其名字一样在每次请求时只执行一次过滤,同样放行操作是执行的filterChain.doFilter()方法。
4、基于Nginx
在Nginx中添加以下的配置来解决跨域
location / { add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Headers X-Requested-With; add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS; if ($request_method = 'OPTIONS') { return 204; } }