解决SpringBoot跨域访问问题的两种详解方法(超详细)

前话
对于前后端分离的项目来说,如果前端项目与后端项目部署在两个不同的域下,那么势必会引起跨域问题的出现。

方法1 JSONP
第一个解决方案就是jsonp,并且以前处理跨域问题基本也是这么处理。
在前端写一个类似于Ajax的异步请求,解析回调函数返回的结果参数,Ajax直接请求普通文件存在跨域无权限访问的问题,不管是静态页面、动态网页、web服务、WCF,只要是跨域请求,一律不准,但是凡是拥有”src” 这个属性的标签都拥有跨域的能力,如<\script>、<\img>、<\iframe>。
( ajax和jsonp这两种技术在调用方式上”看起来”很像,目的也一样,都是请求一个url,然后把服务器返回的数据进行处理,因此jquery和ext等框架都把jsonp作为ajax的一种形式进行了封装。但ajax和jsonp其实本质上是不同的东西。ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加。
出于安全考虑,浏览器会限制脚本中发起的跨站请求。使用 XMLHttpRequest 对象发起 HTTP 请求就必须遵守同源策略(same-origin policy)
)*

如下示例:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>JSONP 实例</title>
    <script src="https://cdn.static.runoob.com/libs/jquery/1.8.3/jquery.js"></script>    
</head>
<body>
<div id="divCustomers"></div>
<script>
$.getJSON("https://www.runoob.com/try/ajax/jsonp.php?jsoncallback=?", function(data) {
    
    var html = '
    '; for(var i = 0; i < data.length; i++) { html += '
  • ' + data[i] + '
  • '
    ; } html += '
'
; $('#divCustomers').html(html); }); </script> </body> </html>
但是jsonp方式也同样有不足,不管是对于前端还是后端来说,写法与我们平常的ajax写法不同,同样后端也需要作出相应的更改。并且,jsonp方式只能通过get请求方式来传递参数,当然也还有其它的不足之处,这里就不一一赘述了。

方法2 通过cors协议解决跨域问题
Spring MVC 从4.2版本开始增加了对CORS的支持。Cross-Origin Resource Sharing(跨域资源共享)。通过它,我们可以决定资源是否能被跨域访问。cors是一个w3c标准,它允许浏览器(目前ie8以下还不能被支持)向我们不同源的服务器发出XmlHttpRequest请求,我们可以继续使用ajax进行请求访问。
在SpringBoot中提供Cros协议配置的方式有两种:

1.通过过滤器实现

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

public class CorsFilter implements Filter {

    private String allowOrigin;
    private String allowMethods;
    private String allowCredentials;
    private String allowHeaders;
    private String exposeHeaders;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        allowOrigin = filterConfig.getInitParameter("allowOrigin");
        allowMethods = filterConfig.getInitParameter("allowMethods");
        allowCredentials = filterConfig.getInitParameter("allowCredentials");
        allowHeaders = filterConfig.getInitParameter("allowHeaders");
        exposeHeaders = filterConfig.getInitParameter("exposeHeaders");
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        if (StringUtils.isNotBlank(allowOrigin)) {
            List<String> allowOriginList = Arrays.asList(allowOrigin.split(","));
            if (CollectionUtils.isNotEmpty(allowOriginList)) {
                String currentOrigin = request.getHeader("Origin");
                if (allowOriginList.contains(currentOrigin)) {
                    response.setHeader("Access-Control-Allow-Origin", currentOrigin);
                }
            }
        }
        if (StringUtils.isNotBlank(allowMethods)) {
            response.setHeader("Access-Control-Allow-Methods", allowMethods);
        }
        if (StringUtils.isNotBlank(allowCredentials)) {
            response.setHeader("Access-Control-Allow-Credentials", allowCredentials);
        }
        if (StringUtils.isNotBlank(allowHeaders)) {
            response.setHeader("Access-Control-Allow-Headers", allowHeaders);
        }
        if (StringUtils.isNotBlank(exposeHeaders)) {
            response.setHeader("Access-Control-Expose-Headers", exposeHeaders);
        }
        filterChain.doFilter(req, res);
    }

    @Override
    public void destroy() {

    }

以上 CorsFilter 将从 web.xml 中读取相关 Filter 初始化参数,并将在处理 HTTP 请求时将这些参数写入对应的 CORS 响应头中,下面大致描述一下这些 CORS 响应头的意义:

  1. Access-Control-Allow-Origin:允许访问的客户端域名,例如:http://web.xxx.com,若为
    *,则表示从任意域都能访问,即不做任何限制。
  2. Access-Control-Allow-Methods:允许访问的方法名,多个方法名用逗号分割,例如:GET,POST,PUT,DELETE,OPTIONS。
  3. Access-Control-Allow-Credentials:是否允许请求带有验证信息,若要获取客户端域下的 cookie
    时,需要将其设置为 true。
  4. Access-Control-Allow-Headers:允许服务端访问的客户端请求头,多个请求头用逗号分割,例如:Content-Type。
  5. Access-Control-Expose-Headers:允许客户端访问的服务端响应头,多个响应头用逗号分割。

需要注意的是,CORS 规范中定义 Access-Control-Allow-Origin 只允许两种取值,要么为 *,要么为具体的域名,也就是说,不支持同时配置多个域名。为了解决跨多个域的问题,需要在代码中做一些处理,这里将 Filter 初始化参数作为一个域名的集合(用逗号分隔),只需从当前请求中获取 Origin 请求头,就知道是从哪个域中发出的请求,若该请求在以上允许的域名集合中,则将其放入 Access-Control-Allow-Origin 响应头,这样跨多个域的问题就轻松解决了。

可以在web.xml 中配置过滤器来生效,如下配置

<filter>
    <filter-name>corsFilter</filter-name>
    <filter-class>com.xxx.api.cors.CorsFilter</filter-class>
    <init-param>
        <param-name>allowOrigin</param-name>
        <param-value>http://web.xxx.com</param-value>
    </init-param>
    <init-param>
        <param-name>allowMethods</param-name>
        <param-value>GET,POST,PUT,DELETE,OPTIONS</param-value>
    </init-param>
    <init-param>
        <param-name>allowCredentials</param-name>
        <param-value>true</param-value>
    </init-param>
    <init-param>
        <param-name>allowHeaders</param-name>
        <param-value>Content-Type</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>corsFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

在springboot中可以配置生效,如下

import com.fpd.order.filter.CorsFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;

@Configuration
public class FilterBeanConfig {

    @Bean
    public FilterRegistrationBean corsFilter(){
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new CorsFilter());
        ArrayList<String> list = new ArrayList<>();
        list.add("*");
        registrationBean.setUrlPatterns(list);
        return registrationBean;
    }
}
  1. 使用@CrossOrigin**注解描述控制器类或方法。更加灵活

在Controller上使用@CrossOrigin注解

	@CrossOrigin(origins = "http://xxx.com", maxAge = 3600)
	@RestController
	@RequestMapping("/")
	public class AccountController {
		...
	}

在方法上使用@CrossOrigin注解

 	@CrossOrigin("http://xxx.com")
    @RequestMapping("/")
    public Result get(@PathVariable Long id) {
        // ...
    }

你可能感兴趣的:(java)