> 文档中心 > 新版 springboot-前后端分离-跨域设置的五种方法以及遇到的坑

新版 springboot-前后端分离-跨域设置的五种方法以及遇到的坑


用到的版本:

springboot 2.6.5
security 5.6.2
spring framework 5.3.17

1 注解 @CrossOrigin (局部)

用在需要跨域的controller接口上或用在某个方法之上
eg:

@CrossOrigin(origins = "http://xxx.com", maxAge = 3600)@RestController@RequestMapping("/account")public class AccountController {// 方法之上的会覆盖类上的@CrossOrigin(origins = "http://xxx2.com", maxAge = 3600)    @GetMapping("/{id}")    public Account retrieve(@PathVariable Long id) { // ...    }    @DeleteMapping("/{id}")    public void remove(@PathVariable Long id) { // ...    }}

其中两个参数
origins : 允许可访问的域列表
maxAge:准备响应前的缓存持续的最大时间(以秒为单位)。

2 实现 Filter 类

package com.zuijin.mall.component;import org.springframework.stereotype.Component;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletResponse;import java.io.IOException;/** * -------------------------------------------- * ClassName:    CorsFilter * CreateBy:     IntelliJ IDEA * Author:醉瑾 * Date:  2022-03-31 * Description : 全局跨域组件 * -------------------------------------------- */@Componentpublic class CorsFilter implements Filter {    @Override    public void init(FilterConfig filterConfig) {    }    @Override    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException { HttpServletResponse response = (HttpServletResponse) servletResponse; //该字段必填。它的值要么是请求时Origin字段的具体值,要么是一个*,表示接受任意域名的请求。 response.setHeader("Access-Control-Allow-Origin", "*"); //该字段必填。它的值是逗号分隔的一个具体的字符串或者*,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。 response.setHeader("Access-Control-Allow-Methods", "*"); //该字段可选,用来指定本次预检请求的有效期,单位为秒。在有效期间,不用发出另一条预检请求。 response.setHeader("Access-Control-Max-Age", "3600"); //该字段可选。它的值是一个布尔值,表示是否允许发送Cookie.默认情况下,不发生Cookie,即:false。对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json,这个值只能设为true。如果服务器不要浏览器发送Cookie,删除该字段即可。 response.setHeader("Access-Control-Allow-Credentials", "true"); //该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。 response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); filterChain.doFilter(servletRequest, servletResponse);    }    @Override    public void destroy() {    }}

3、实现 WebMvcConfigurer 类

package com.zuijin.mall.config;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.CorsRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/** * -------------------------------------------- * ClassName:    CorsConfig * CreateBy:     IntelliJ IDEA * Author:醉瑾 * Date:  2022-03-30 * Description : 跨域配置 * -------------------------------------------- */@Configurationpublic class CorsConfig implements WebMvcConfigurer {    @Override    public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**")  //.allowedOrigins("*")  .allowedOriginPatterns("*")  .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")  .allowedHeaders("*")  .allowCredentials(true) // 这里设置为true,allowedOrigins("*")方法不能再设置为通配符  .maxAge(3600);    }}

4、注入CorsFilter bean,返回一个新的corsFilter

package com.zuijin.mall.config;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 java.util.ArrayList;import java.util.Collections;import java.util.List;/** * -------------------------------------------- * ClassName:    GlobalCorsConfig * CreateBy:     IntelliJ IDEA * Author:醉瑾 * Date:  2022-03-30 * Description : 全局跨域配置 * -------------------------------------------- */@Configurationpublic class GlobalCorsConfig {    private CorsConfiguration buildConfig() { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.addAllowedHeader(CorsConfiguration.ALL); // 添加要允许的实际请求标头。 corsConfiguration.addAllowedMethod(CorsConfiguration.ALL); // 允许任何方法(post、get等) // corsConfiguration.addAllowedOrigin("http://localhost:8090/"); corsConfiguration.addAllowedOriginPattern(CorsConfiguration.ALL); // 允许任何域名使用 //corsConfiguration.setAllowedOriginPatterns(Collections.singletonList(CorsConfiguration.ALL)); corsConfiguration.setAllowCredentials(true); // 此处设置为 true,则 addAllowedOrigin不能使用通配符 return corsConfiguration;    }    @Bean    public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", buildConfig()); // 对接口配置跨域设置 return new CorsFilter(source);    }}

5,在 SecurityConfig配置文件中修改

package com.zuijin.mall.config;import com.zuijin.mall.component.JwtAuthenticationTokenFilter;import com.zuijin.mall.component.RestAuthenticationEntryPoint;import com.zuijin.mall.component.RestfulAccessDeniedHandler;import com.zuijin.mall.dto.AdminUserDetails;import com.zuijin.mall.mbg.model.UmsAdmin;import com.zuijin.mall.mbg.model.UmsPermission;import com.zuijin.mall.service.UmsAdminService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Lazy;import org.springframework.http.HttpMethod;import org.springframework.security.authentication.AuthenticationManager;import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.config.http.SessionCreationPolicy;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.core.userdetails.UsernameNotFoundException;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.security.crypto.password.PasswordEncoder;import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;import org.springframework.web.cors.CorsConfiguration;import org.springframework.web.cors.UrlBasedCorsConfigurationSource;import javax.annotation.Resource;import java.util.List;/** * -------------------------------------------- * ClassName:    SecurityConfig * CreateBy:     IntelliJ IDEA * Author:醉瑾 * Date:  2022-03-28 * Description : security配置文件 * -------------------------------------------- */@Configuration@EnableWebSecurity@EnableGlobalMethodSecurity(prePostEnabled = true) // 曾经报错public class SecurityConfig extends WebSecurityConfigurerAdapter {    @Lazy // 懒加载解决循环依赖问题    @Resource    private UmsAdminService adminService;    @Resource    private RestfulAccessDeniedHandler restfulAccessDeniedHandler;    @Resource    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;    @Override    protected void configure(HttpSecurity http) throws Exception { http.csrf()// 由于使用的是JWT,我们这里不需要csrf  .disable()  .sessionManagement()// 基于token,所以不需要session  .sessionCreationPolicy(SessionCreationPolicy.STATELESS)  .and()  .authorizeRequests()  .antMatchers(HttpMethod.GET, // 允许对于网站静态资源的无授权访问,swagger   "/",   "/*.html",   "/favicon.ico",   "/**/*.html",   "/**/*.css",   "/**/*.js",   "/swagger-resources/**",   "/v3/**",   "/swagger-ui/**",   "/webjars/**",   "/doc.html"  )  .permitAll()  .antMatchers("/admin/login", "/admin/register")// 对登录注册要允许匿名访问  .permitAll()  .antMatchers(HttpMethod.OPTIONS) //跨域请求会先进行一次options请求  .permitAll()  // .antMatchers("/**")//测试时全部运行访问 // 之前注释  // .permitAll()    // 之前注释  .anyRequest() // 除上面外的所有请求全部需要鉴权认证  .authenticated();// 跨域配置 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.addAllowedHeader(CorsConfiguration.ALL); // 添加要允许的实际请求标头。 corsConfiguration.addAllowedMethod(CorsConfiguration.ALL); // 允许任何方法(post、get等) corsConfiguration.addAllowedOriginPattern(CorsConfiguration.ALL); // 允许任何域名使用 或 setAllowedOriginPatterns //corsConfiguration.setAllowedOriginPatterns(Collections.singletonList(CorsConfiguration.ALL)); corsConfiguration.setAllowCredentials(true); // 此处设置为 true,则 addAllowedOrigin不能使用通配符 source.registerCorsConfiguration("/**", corsConfiguration); http.cors().configurationSource(source); // 禁用缓存 http.headers().cacheControl(); // 添加JWT filter http.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class); //添加自定义未授权和未登录结果返回 http.exceptionHandling()  .accessDeniedHandler(restfulAccessDeniedHandler)  .authenticationEntryPoint(restAuthenticationEntryPoint);    }    @Override    protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService())  .passwordEncoder(passwordEncoder());    }    @Bean    public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder();    }    @Bean    public UserDetailsService userDetailsService() { //获取登录用户信息 return username -> adminService.loadUserByUsername(username);    }    @Bean    public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() { return new JwtAuthenticationTokenFilter();    }    @Bean    @Override    public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean();    }}

注意,跨域不成功可能是:

1、是springMVC的版本要在4.2或以上版本才支持@CrossOrigin
2、在Controller注解上方添加@CrossOrigin注解后,仍然出现跨域问题,解决方案之一就是:在@RequestMapping注解中没有指定Get、Post方式
3、非@CrossOrigin没有解决跨域请求问题,如后端 使用 vue 时这样写,BASE_URL=“localhost:8080”,正确应该这样写:BASE_URL=“http://localhost:8080”
4、同时配置多个跨域配置可能会出现返回状态码 200 ,但是接收不到响应的情况
5、高版本设置 corsConfiguration.setAllowCredentials(true); // 此处设置为 true,则 addAllowedOrigin不能使用通配符
6、如果在 security 中配置 http.cors().disable();则其他跨域配置不能生效。

每个人遇到的问题都各不相同,如果没有解决大家问题不用怼我,大家共同探讨!!