【已解决】Spring Boot解决跨域问题跨域-看这一篇就够了。使用vue+axios+spring boot前后端分离项目时会出现跨域问题怎么解决

  • 作者: 凯哥Java(公众号:凯哥Java)
  • 经验分享
  • 时间:2023-08-13 18:11
  • 2260人已阅读
简介 本文通过多种方案介绍了解决spring boot跨域问题解决方案

🔔🔔🔔好消息!好消息!🔔🔔🔔

有需要的朋友👉:联系凯哥 微信号 kaigejava2022

场景描述:

使用vue+axios+spring boot前后端分离项目时会出现跨域问题

解决方案,这里使用了四种方案来解决。分别是:

一: 针对单个接口,使用注解@CrossOrigin

二: 全局配置

三: 自定义跨域过滤器

四:通过Response跨域

五:重写ResponseBodyAdvice

六: 使用nginx反向代理服务器解决跨域问题

下面来详细介绍每种方案的配置:

方案一:针对单个接口,使用注解@CrossOrigin

比如我A接口需要支持跨域,在A接口的类上使用@CrossOrigin(origins = "*",maxAge = 3600)

具体代码:

  @RestController
  @RequestMapping("/user")
  @RequiredArgsConstructor
  @CrossOrigin(origins = "*",maxAge = 3600)
 public class UserController {
     final UserMapper userMapper;
	 @GetMapping("/getOne/{id}")
	 public User getOne(@PathVariable("id") Integer id) {
	return userMapper.getById(id);
	}
	}

此种方案的优缺点:

优缺点分析此方式虽然虽然实现(跨域)比较简单,但细心的朋友也能发现,使用此方式只能实现局部跨域,当一个项目中存在多个类的话,使用此方式就会比较麻烦(需要给所有类上都添加此注解)。


方案二:全局配置


import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
@Configuration // 一定不要忽略此注解
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**") // 所有接口
                .allowCredentials(true) // 是否发送 Cookie
                .allowedOriginPatterns("*") // 支持域
                .allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"}) // 支持方法
                .allowedHeaders("*")
                .exposedHeaders("*");
    }
}


方案三:自定义跨域过滤器

此实现方式和上一种实现方式类似,它也可以实现全局跨域,它的具体实现代码如下:

3.1:编写filter代码如下:

public class CrosFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // TODO Auto-generated method stub

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // TODO Auto-generated method stub

        HttpServletResponse res = (HttpServletResponse) response;

        HttpServletRequest req = (HttpServletRequest) request;

        String origin = req.getHeader("Origin");

        if (!org.springframework.util.StringUtils.isEmpty(origin)) {
            //带cookie的时候,origin必须是全匹配,不能使用*
            res.addHeader("Access-Control-Allow-Origin", origin);
        }

        res.addHeader("Access-Control-Allow-Methods", "*");

        String headers = req.getHeader("Access-Control-Request-Headers");

        // 支持所有自定义头
        if (!org.springframework.util.StringUtils.isEmpty(headers)) {
            res.addHeader("Access-Control-Allow-Headers", headers);
        }

        res.addHeader("Access-Control-Max-Age", "3600");

        // enable cookie
        res.addHeader("Access-Control-Allow-Credentials", "true");

        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        // TODO Auto-generated method stub

    }

}

3.2:将上面编写的过滤器注册到容器中

/**
     * @desc 注册自定义跨域过滤器
     * @author guozhongyao
     * @date 2020/3/30 15:52
     */
    @Bean
    public FilterRegistrationBean registerFilter(){
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.addUrlPatterns("/*");
        bean.setFilter(new CrosFilter());
        return bean;
    }


解决方案4:通过 Response 跨域

此方式是解决跨域问题最原始的方式,但它可以支持任意的 Spring Boot 版本(早期的 Spring Boot 版本也是支持的)。但此方式也是局部跨域,它应用的范围最小,设置的是方法级别的跨域,它的具体实现代码如下:

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
 
@RestController
public class TestController {
    @RequestMapping("/test")
    public HashMap<String, Object> test(HttpServletResponse response) {
        // 设置跨域
        response.setHeader("Access-Control-Allow-Origin", "*");
        return new HashMap<String, Object>() {{
            put("state", 200);
            put("data", "success");
            put("msg", "");
        }};
    }
}


解决方案5:ResponseBodyAdvice

通过重写 ResponseBodyAdvice 接口中的 beforeBodyWrite(返回之前重写)方法,我们可以对所有的接口进行跨域设置,它的具体实现代码如下:

import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
 
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    /**
     * 内容是否需要重写(通过此方法可以选择性部分控制器和方法进行重写)
     * 返回 true 表示重写
     */
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }
    /**
     * 方法返回之前调用此方法
     */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class selectedConverterType, ServerHttpRequest request,
                                  ServerHttpResponse response) {
        // 设置跨域
        response.getHeaders().set("Access-Control-Allow-Origin", "*");
        return body;
    }
}

此实现方式也是全局跨域,它对整个项目中的所有接口有效。


方案六:使用nginx反向代理服务器解决跨域问题

1, 在nginx的conf目录下,创建vhosts文件夹

2,  在nginx.conf 文件中添加 include vhosts/*.conf;

478bcbe7774cffd3a00c345992dc2bf0.png

 3, vhost目录下创建 abc.conf文件

08dceec9a170f49dfccc8414e741b858.png


原理分析

为什么通过以上方法设置之后,就可以实现不同项目之间的正常交互呢?这个问题的答案也很简单,我们之前在说跨域时讲到:“跨域问题本质是浏览器的行为,它的初衷是为了保证用户的访问安全,防止恶意网站窃取数据”,那想要解决跨域问题就变得很简单了,只需要告诉浏览器这是一个安全的请求,“我是自己人”就行了,那怎么告诉浏览器这是一个正常的请求呢?只需要在返回头中设置“Access-Control-Allow-Origin”参数即可解决跨域问题,此参数就是用来表示允许跨域访问的原始域名的,当设置为“*”时,表示允许所有站点跨域访问,如下图所示:

c5657cde7a64e5cb5d7aa09a6d92922b.png

所以以上 六种解决跨域问题的本质都是给响应头中加了一个 Access-Control-Allow-Origin 的响应头而已


总结

跨域问题的本质是浏览器为了保证用户的一种安全拦截机制,想要解决跨域问题,只需要告诉浏览器“我是自己人,不要拦我”就行。它的常见实现方式有 5 种:通过注解实现局部跨域、通过配置文件实现全局跨域、通过 CorsFilter 对象实现全局跨域、通过 Response 对象实现局部跨域,通过 ResponseBodyAdvice 实现全局跨域。



TopTop