Spring Boot 2.x(九):遇到跨域不再慌

什么是跨域

首先,我们需要了解一下一个 URL 是怎么组成的:

// 协议 + 域名(子域名 + 主域名) + 端口号 + 资源地址
http: + // + www.baidu.com + :8080/

只要协议,子域名,主域名,端口号这四项组成部分中有一项不同,就可以认为是不同的域,不同的域之间互相访问资源,就被称之为跨域。

随着前后端分离开发的越来越普及,会经常遇到跨域的问题,当我们在浏览器中看到这样的错误时,就需要意识到遇到了跨域:

解决跨域的几种方案

首先,我们使用vue-cli来快速构建一个前端项目,然后使用axios来向后台发送 ajax 请求。然后在控制台中打印出返回信息。这里就不再多做赘述,后面我会单独写一篇文章来讲一下如何使用 vue-cli 快速创建一个 vue 项目。

这里不再讲解使用jsonp的方式来解决跨域,因为jsonp方式只能通过get请求方式来传递参数,而且有一些不便之处。

下面的几种方式都是通过跨域访问技术 CORS(Cross-Origin Resource Sharing)来解决的。具体的实现原理我们不做深入的探究,这节课的目的是解决跨域问题 ~

方法一:注解

在 Spring Boot 中给我们提供了一个注解@CrossOrigin来实现跨域,这个注解可以实现方法级别的细粒度的跨域控制。我们可以在类或者方添加该注解,如果在类上添加该注解,该类下的所有接口都可以通过跨域访问,如果在方法上添加注解,那么仅仅只限于加注解的方法可以访问。

@RestController
@RequestMapping("/user")
@CrossOrigin
public class UserController {
   @Autowired
    private UserService userService;

@RequestMapping("/findAll")
public Object findAll(){
return userService.list();
}
}

现在再去测试一下:

Bingo,成功!

方法二:实现 WebMvcConfigurer

这里可以通过实现WebMvcConfigurer接口中的addCorsMappings()方法来实现跨域。

@Configuration
class CORSConfiguration implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**");
    }
}

下面我们将刚刚加上的注解给注释掉,看看能不能访问到这个接口:

不出我们所料,果然还是可以的 ~

方法三:Filter

我们可以通过实现 Fiter 接口在请求中添加一些 Header 来解决跨域的问题:

@Component
public class CORSFilter implements Filter {
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">doFilter</span><span class="hljs-params">(ServletRequest request, ServletResponse response, FilterChain chain)</span>
        <span class="hljs-keyword">throws</span> IOException, ServletException {
    <span class="hljs-type">HttpServletResponse</span> <span class="hljs-variable">res</span> <span class="hljs-operator">=</span> (HttpServletResponse) response;
    res.addHeader(<span class="hljs-string">"Access-Control-Allow-Credentials"</span>, <span class="hljs-string">"true"</span>);
    res.addHeader(<span class="hljs-string">"Access-Control-Allow-Origin"</span>, <span class="hljs-string">"*"</span>);
    res.addHeader(<span class="hljs-string">"Access-Control-Allow-Methods"</span>, <span class="hljs-string">"GET, POST, DELETE, PUT"</span>);
    res.addHeader(<span class="hljs-string">"Access-Control-Allow-Headers"</span>, <span class="hljs-string">"Content-Type,X-CAF-Authorization-Token,sessionToken,X-TOKEN"</span>);
    <span class="hljs-keyword">if</span> (((HttpServletRequest) request).getMethod().equals(<span class="hljs-string">"OPTIONS"</span>)) {
        response.getWriter().println(<span class="hljs-string">"ok"</span>);
        <span class="hljs-keyword">return</span>;
    }
    chain.doFilter(request, response);
}
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">destroy</span><span class="hljs-params">()</span> {
}
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">init</span><span class="hljs-params">(FilterConfig filterConfig)</span> <span class="hljs-keyword">throws</span> ServletException {
}

}

不出意外的话,应该也可以在控制台获取到返回信息。

Nginx 配置解决跨域问题

如果我们在项目中使用了 Nginx,可以在 Nginx 中添加以下的配置来解决跨域(原理其实和 Filter 类似,只不过把活儿丢给了运维🤔)

location / {
   add_header Access-Control-Allow-Origin *;
   add_header Access-Control-Allow-Headers X-Requested-With;
   add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;

if ($request_method = 'OPTIONS') {
return 204;
}
}

原创文章,才疏学浅,如有不对之处,万望告知!


公众号

您的推荐就是对我最大的支持!