新版 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();则其他跨域配置不能生效。