新版 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 : 全局跨域组件
 * --------------------------------------------
 */
@Component
public 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里面指定。
        // 假设前端 header 中带有 token 字样,这个配置就应该 有 token,或者直接配成 * 通配符
        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 : 跨域配置
 * --------------------------------------------
 */
@Configuration
public 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 : 全局跨域配置
 * --------------------------------------------
 */
@Configuration
public 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();则其他跨域配置不能生效。
7、Access-Control-Allow-Headers 没有设置允许前端自定义的 header 值。解决方法:加上前端自定义header 值,或使用 通配符 *

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏至xz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值