济南 手机网站制作,广州地址设计网站,台州做网站那家好,手游游戏源码资源网前言
浏览器有跨域限制#xff0c;非同源策略 (协议、主机名或端口不同) 被视为跨域请求#xff0c;解决跨域有跨域资源共享(CORS)、反向代理和 JSONP的方式。本篇通过 SpringBoot 的资源共享配置 (CORS) 来解决前后端分离项目的跨域#xff0c;以及从原理上去解决跨域配置…前言
浏览器有跨域限制非同源策略 (协议、主机名或端口不同) 被视为跨域请求解决跨域有跨域资源共享(CORS)、反向代理和 JSONP的方式。本篇通过 SpringBoot 的资源共享配置 (CORS) 来解决前后端分离项目的跨域以及从原理上去解决跨域配置不生效的问题。
准备工作
https://gitee.com/youlaiorg/vue3-element-admin) 默认通过 vite proxy 前端反向代理解决跨域如果想关闭方向代理只需修改 baseURL 即可
// request.ts
const service axios.create({//baseURL: import.meta.env.VITE_APP_BASE_API, // 前端反向代理解决跨域的配置baseURL: http://localhost:8989, // 后端通过配置CORS解决跨域的配置, http://localhost:8989 是后端接口地址timeout: 50000,headers: { Content-Type: application/json;charsetutf-8 }
});配置 CORS 允许跨域
一般情况在项目添加以下配置即可解决浏览器跨域限制。
/*** CORS 资源共享配置** author haoxr* date 2022/10/24*/
Configuration
public class CorsConfig {Beanpublic CorsFilter corsFilter() {CorsConfiguration corsConfiguration new CorsConfiguration();//1.允许任何来源corsConfiguration.setAllowedOriginPatterns(Collections.singletonList(*));//2.允许任何请求头corsConfiguration.addAllowedHeader(CorsConfiguration.ALL);//3.允许任何方法corsConfiguration.addAllowedMethod(CorsConfiguration.ALL);//4.允许凭证corsConfiguration.setAllowCredentials(true);UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration(/**, corsConfiguration);return new CorsFilter(source);}
}CORS 允许跨域原理
CorsFilter 读取 CorsConfig 配置通过 DefaultCorsProcessor 给 response 响应头添加 Access-Control-Allow-* 以允许跨域请求能够被成功处理。
响应头参数作用Access-Control-Allow-Origin允许访问的源地址Access-Control-Allow-Methods允许访问的请求方法Access-Control-Allow-Headers允许访问的请求头Access-Control-Allow-Credentials是否允许发送 Cookie 等身份凭证Access-Control-Max-Age缓存预检请求的时间
核心是 DefaultCorsProcessor# handleInternal 方法 CORS 配置失效原理分析
但。。。有的项目按照如上配置允许跨域请求成功了但有些项目却不生效
其实就是一个结论有中断响应的过滤器在 CorsFilter 之前执行了也就无法执行到 CorsFilter自然 CorsConfiguration 中的配置形同虚设。
常见的场景项目中使用了 Spring Security 安全框架导致 CORS 跨域配置失效。
接下来就 Spring Security 导致 CORS 配置失效展开分析。
在 ApplicationFilterChain#internalDoFilter 添加断点然后通过改造后 (移除反向代理) 的 发出跨域请求。 可以看出 SpringSecurityFilterChain 是先于 CorsFilter 执行的重点, 如果是跨域请求浏览器会在正式请求前发出一次预检请求 (OPTIONS)判断服务器是否允许跨域。
跨域请求没到达 CorsFilter 过滤器就先被 Spring Security 的过滤器给拦截了要知道预检 OPTIONS 请求是不带 token 的所以响应 401 未认证的错误。预检请求失败导致后面的请求响应会被浏览器拦截。 CORS 配置失效解决方案
根据配置失效原理分析有两个解决方案 解决方案一 配置 CorsFilter 优先于 SpringSecurityFilter 执行 解决方案二 放行预检 OPTIONS 请求 基础 CORS 配置。
解决方案一 (推荐) 配置 CorsFilter 优先于 SpringSecurityFilter 执行 Spring Security 过滤器是通过 SecurityFilterAutoConfiguration 的 DelegatingFilterProxyRegistrationBean 注册到 servletContext 上下文其中过滤器的顺序属性 Order 读取的 是 SecurityProperties 的默认配置也就是 -100 SpringBoot 可以通过 FilterRegistrationBean 来对 Filter 自定义注册排序, 设置 Order 小于 SpringSecurity 的 -100 即可。完整配置如下
/*** CORS资源共享配置** author haoxr* date 2023/4/17*/
Configuration
public class CorsConfig {Beanpublic FilterRegistrationBean filterRegistrationBean() {CorsConfiguration corsConfiguration new CorsConfiguration();//1.允许任何来源corsConfiguration.setAllowedOriginPatterns(Collections.singletonList(*));//2.允许任何请求头corsConfiguration.addAllowedHeader(CorsConfiguration.ALL);//3.允许任何方法corsConfiguration.addAllowedMethod(CorsConfiguration.ALL);//4.允许凭证corsConfiguration.setAllowCredentials(true);UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration(/**, corsConfiguration);CorsFilter corsFilter new CorsFilter(source);FilterRegistrationBeanCorsFilter filterRegistrationBeannew FilterRegistrationBean(corsFilter);filterRegistrationBean.setOrder(-101); // 小于 SpringSecurity Filter的 Order(-100) 即可return filterRegistrationBean;}
}可以看到不同源的跨域请求能够成功响应。 解决方案二 放行预检 OPTIONS 请求 基础 CORS 配置 SecurityConfig 放行 OPTIONS 预检请求配置 SecurityConfig 配置源码
Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http ...// 走 Spring Security 过滤器链的放行配置.requestMatchers(HttpMethod.OPTIONS,/**).permitAll() // 放行预检请求.anyRequest().authenticated();return http.build();}Beanpublic WebSecurityCustomizer webSecurityCustomizer() {// 不走过滤器链的放行配置return (web) - web.ignoring().requestMatchers(HttpMethod.OPTIONS,/**) // 放行预检请求}或者 //放行 options 的请求if (OPTIONS.equals(request.getMethod())) {filterChain.doFilter(request, servletResponse);}基础的跨域共享配置
Configuration
public class CorsConfig {Beanpublic CorsFilter corsFilter() {CorsConfiguration corsConfiguration new CorsConfiguration();//1.允许任何来源corsConfiguration.setAllowedOriginPatterns(Collections.singletonList(*));//2.允许任何请求头corsConfiguration.addAllowedHeader(CorsConfiguration.ALL);//3.允许任何方法corsConfiguration.addAllowedMethod(CorsConfiguration.ALL);//4.允许凭证corsConfiguration.setAllowCredentials(true);UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration(/**, corsConfiguration);return new CorsFilter(source);}}另外有自定义过滤器 例如VerifyCodeFilter通过 response.getWriter().print() 响应给浏览器也是不走后面的 CorsFilter 过滤器所以需要设置响应头
// ResponseUtils# writeErrMsg
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setHeader(Access-Control-Allow-Origin,*);
response.getWriter().print(JSONUtil.toJsonStr(Result.failed(resultCode)));