前后端分离项目,解决跨域问题

一、什么时候是跨域问题?什么时候会发生跨域问题?

跨域问题是前后端项目中非常常见的一个问题,举例来说,编程猫(codingmore)学习网站的前端服务 跑在8080端口下,后端项目跑在9002端口下,那么前端在请求后端接口的时候就会出现跨域问题。
在这里插入图片描述

403Forbidden是http协议中的一个状态码(Status Code),意味着后端服务虽然成功解析了请求,但前端却没有访问该资源的权限。
那么怎么解决这个问题呢? 通常有两个思路:

  • 前端使用Node.js代理(开发环境下,生产环境可以使用Nginx替代)
  • 或者后端开启跨域资源共享

二、关于跨域

跨域对于前后端开发者来说,就像一块狗皮膏药,无论面试还是开发中,都会经常遇到。
之所以出现跨域问题,是因为浏览器的同源策略,为了隔离潜在的恶意文件,为了防御来自一些其他的恶意攻击,浏览器限制了从同一个源加载的文档或脚本与来自另一个源的资源进行交互。
前面我们提到了,前段跑在8080端口下,后端跑在9002端口下,这种情况就属于不同的源(域名不同、协议不同、端口不同),所以8080端口下的前端请求直接访问9002端口下的后端端口时就访问失败了。

那正确的打开方式是什么呢?我们前面也提到了,前端使用了Node.js代理或者后端开启跨域资源共享,我们一一来实践下。

三、Node.js代理解决跨域问题

 在Node.js出现之前,JavaScript编写的程序通常需要在用户的浏览器上执行,Node.js出现后,JavaScript也能用于服务端编程了。Node.js一系列的内置模块使得程序可以脱离Apache这种Web服务作为独立的服务器执行。
 我们使用Node.js来解决跨域问题的思路就是,在本地创建一个虚拟服务器,对8080端口下的前端请求进行代理,同时接受9002端口下的服务器端相应,这样服务端和服务端进行数据的交互就不会出现跨域问题了。

3.1 第一步,配置Node.js代理服务

module.exports = {
  dev: {
    // Paths
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',
    proxyTable: {
      '/api': {
        target: 'http://localhost:9002', // 你请求的第三方接口
        changeOrigin: false, // 在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样服务端和服务端进行数据的交互就不会有跨域问题
        pathRewrite: { // 路径重写,
          '^/api': '' // 替换target中的请求地址,也就是说以后你在请求http://api.codingmore.top/v2/XXXXX这个地址的时候直接写成/api即可。
        }
      },
    },
}

3.2 第二步,配置前端访问请求路径

module.exports = {
  dev: {
    // Paths
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',
    proxyTable: {
      '/api': {
        target: 'http://localhost:9002', // 你请求的第三方接口
        changeOrigin: false, // 在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样服务端和服务端进行数据的交互就不会有跨域问题
        pathRewrite: { // 路径重写,
          '^/api': '' // 替换target中的请求地址,也就是说以后你在请求http://api.codingmore.top/v2/XXXXX这个地址的时候直接写成/api即可。
        }
      },
    },
}

3.3 重启前端服务

再次点击登录按钮,可以看待请求的URL发生了改变,原来是 http://localhost:9002/users/login,现在是http://localhost:8080/api/users/login。与此同时,可以看到多了一个Remote Address,端口也是8080,也就是说经过Node.js的代理,前后端的交互在一个源下面了,这样就不会发生跨域问题了。
同时可以看到服务端返回的状态码变成了200,表示请求成功。
在这里插入图片描述

四、开启跨域资源共享

跨域资源共享,也就是Cross-Origin Resource Sharing,简拼为CORS,是一种基于HTTP头信息的机制,通过允许服务器标识除了它自己以外的资源,从而实现跨域访问。

4.1 第一步,开启CORS支持

在SpringBoot应用中,加入CORS的支持简单到不忍直视,添加一个配置类就可以了。

@Configuration
public class GlobalCorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        // 设置你要允许的网站域名
        config.addAllowedOrigin("http://localhost:8080");
        //允许跨域发送cookie
        config.setAllowCredentials(true);
        //放行全部原始头信息
        config.addAllowedHeader("*");
        //允许所有请求方法跨域调用
        config.addAllowedMethod("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}

4.2 第二步 重启后端服务,再次点击登录按钮,发现请求已经可以正常访问了。

在这里插入图片描述

本例中,后端返回 Access-Control-Allow-Origin: http://localhost:8080 就表示,跑在 9002 端口下的后端接口可以被 8080 端口的前端请求访问。
如果允许所有域名进行跨域调用的话,只需改变一行代码即可。

//允许所有域名进行跨域调用
config.addAllowedOriginPattern("*");
// 设置你要允许的网站域名
//        config.addAllowedOrigin("http://localhost:8080");

要是还想把工资提高了几千M,请再坚持下,读完下面这一小丢丢的内容(相关概念采自阮一峰大神的博客)

对于 login 这种简单的请求来说,它们是不会触发 CORS 预检的,因此不需要在服务器端增加其他配置就可以了。那什么是简单请求呢?

4.3 什么是简单请求?

  • 1、请求方法是以下三种方法之一:
    HEAD
    GET
    POST
  • 2、HTTP的头信息不超出以下几种字段:
    Accept
    Accept-Language
    Content-Language
    Last-Event-ID
    Content-Type:只限于三个值 application/x-www-form-urlencoded、multipart/form-data、text/plain

4.4 非简单请求

那对于会触发 CORS 预检的非简单请求(比如说请求方法是 PUT 或 DELETE,或者 Content-Type 字段的类型是
application/json,或者请求消息头包含了一些自定义的字段),该怎么办呢? 非简单请求在正式通信之前,会增加一次 HTTP
查询请求,称为“预检”请求。预检请求通过后,才会返回正常的响应内容。 拿编程猫的文章管理页来举例,该页面会向后端发起一个
posts/queryPageable 的分页查询,该请求包含了一个自定义的消息头
Authorization,于是浏览器认为该请求是一个非简单请求,然后就会自动发起一次 OPTIONS 请求,但由于我们的 Spring
Boot 项目整合了 SpringsScurity
安全管理框架,没有对OPTIONS请求放开登录认证,导致验证失败,文章分页请求的响应数据就没有返回回来。
在这里插入图片描述

第三步,通过一下代码给OPTIONS请求放行。

public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity
                .authorizeRequests();
        //允许跨域请求的OPTIONS请求
        registry.antMatchers(HttpMethod.OPTIONS)
                .permitAll();
    }
}

再次重启后端服务,重新访问文章列表接口,发现有响应数据了。
在这里插入图片描述

非简单请求必须首先使用OPTIONS请求方法发起一个预检请求到服务器端,以获知服务器是否允许该实际请求。"预检请求“的使用,避免了跨域请求对服务器的用户数据造成未预期的影响。
"预检请求“的使用,避免了跨域请求对服务器的用户数据造成未预期的影响。
在这里插入图片描述

第二章,发起正式请求:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

猿小许

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

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

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

打赏作者

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

抵扣说明:

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

余额充值