后端跨域问题的处理

问题描述

在做前后端分离的项目时,很有可能会遇到这样一种情况:

就是在游览器中请求后端的接口,出现了 CORS error 错误

在这里插入图片描述

报错信息如下:

Access to XMLHttpRequest at 'http://localhost:8860/user/auth/login' from origin 'http://localhost:5173' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

大概的意思就是:

跨源资源共享(CORS)策略阻止了来自端口 5173 的前端应用对运行在端口 8860 的后端服务的 XMLHttpRequest 请求


原因分析

它的产生并不是因为后端代码出错了,也不是因为前端调用时有问题,而是因为游览器的安全机制(同源策略限制)所导致的,是一个很典型的跨域问题

那游览器的同源策略是什么?

浏览器的 同源策略(Same-origin policy) 是一种安全策略,它规定了从同一个 源(origin) 加载的文档或脚本如何与来自另一个源的资源进行交互。这是浏览器提供的一个重要的安全机制,用于隔离潜在恶意文档,减少可能被攻击的媒介。

同源是指 协议 + 域名 + 端口 三者相同,也就是说,如果两个页面的协议、域名或端口中有任何一个不相同,那么它们就是不同源的。

同源策略主要限制了以下几个方面:

  • Cookie、LocalStorage 和 IndexedDB:无法读取不同源的 Cookie、LocalStorageIndexedDB,这防止了恶意网站窃取或篡改用户的敏感数据
  • DOM:无法操作不同源的页面的 DOM。这确保了恶意网站不能篡改或窃取其他网站的内容
  • AJAX 请求:默认情况下,无法发送 AJAX 请求到不同源的服务器,这防止了恶意网站发起跨站请求伪造( CSRF )攻击

什么是跨域?

跨域就是违法了同源策略,当一个页面去请求另外一个 URL 时,两者 URL协议 + 域名 + 端口 出现不一致

回到上述案例,从控制台中打印的错误信息就能看出,在 http://localhost:5173 的服务中访问了 http://localhost:8860 下的 /user/auth/login 接口,其端口就出现了不一致,出现了跨域


解决方案

现在知道了这种错误是因为跨域问题造成的,那么如何解决这种跨域问题呢?

解决跨域的方法有很多,这里简单介绍以下三种方法:

  • 方案一:Jsonp

    这种方法是早期的一种跨域解决方案,它对各个游览器的版本兼容性做得比较好,但是实现需要前端跟后端都去写相应的代码来进行支持,耦合度高,而且仅支持 GET 请求,所有不常用目前也不推荐用

    比如说在前端使用 ajax 去发送一个请求,需要指定 dataTypeJsonp,这时请求就会自动加上 callback=xxx 这样的参数,xxx 会作为密钥的形式传到后端 ,后端再将 xxx 返回给前端,这就相当于前后端做了一个校验,使得游览器认可这种跨域请求,就可以进行跨域访问了

    前端代码示范:

    $.ajax((
    	url: 'http://localhost:8860/user/auth/login',
    	dataTpe: "jsonp",
    	// Jsonp: 'callback', // 不指定默认 callback
    	// JsonpCallBack: "xxx", // 不指定自动生成
    	type: 'GET',
    	success: function(result) {
    		alert(result.data)
    	}
    ));
    

    后端代码示范:

    	@GetMapping("/jsonp/{id}")
    	public JSONPObject getUser(@PathVariable Integer id, String callback) {
    		return new JSONPObject(callback, new Result<>(200, "success", data))
    	}
    
  • 方式二:Proxy 代理

    使用代理服务器处理跨域问题的基本思路是,在前端和后端服务之间设置一个代理服务器。这个代理服务器位于前端应用所在的域上,因此可以绕过浏览器的同源策略限制。前端应用向代理服务器发送请求,代理服务器再将这些请求转发给实际的后端服务。后端服务处理请求后,将响应返回给代理服务器,代理服务器再将响应转发给前端应用。这样,前端应用看起来就像是直接与后端服务通信,但实际上所有的通信都是通过代理服务器进行的

    现在前端的框架一般都带有 proxy 反向代理功能,就是前端开发的时候一般会在本地启动一个 node.js 服务,让 node 服务去请求接口,然后再把数据传给游览器,就相当于在本地起了一个服务作为中转站

    示例:在 vite.config.ts 文件中配置代理

    export default defineConfig({
      plugins: [vue()],
      server: {
       // 设置项目端口
       port: 5173,
      // 运行时自动打开游览器
      open: true,
      proxy: {
        '/api': {
           // 目标服务器地址
           target: 'http://localhost:8860',
           // 是否改变源地址
           changeOrigin: true,
           // 重写路径
           rewrite: (path) => path.replace(new RegExp('^' + '/api'), ''),
         },
       },
      },
    })
    
  • 方式三:跨域请求 CORS

    CORS(Cross-Origin Resource Sharing) 是一种计算机安全机制,它允许不同源(即协议、域名和端口不同)的 Web 站点进行资源共享。由于浏览器的同源安全策略,通常情况下,一个域的脚本无法直接访问另一个域的资源。CORS 通过在服务器端设置特定的 HTTP 头部,来告诉浏览器:“我允许这个域的脚本访问我的资源”。这样,浏览器就会解除对跨域请求的限制,从而允许前端应用从不同的域请求资源。

    在这里插入图片描述

    CORS 主要有两种模型:

    • 简单模型: 支持 GET、POST、PUT、DELETE 请求,但不允许自定义 header 且会忽略 cookiesPOST 数据格式也有限制,主要支持 text/plain、application/x-www-form-urlencodedmultipart/form-data。其中 text/plain 是默认支持的,后两者需要与服务器进行预检请求和协商
    • 协商模型/预检请求: 当发出如 POST 请求时,浏览器会首先发出一个 OPTIONS 请求进行预检。如果服务器返回 Access-Control-Allow-OriginAccess-Control-Allow-Methods 等头部,并同意来自某个域的请求,浏览器就会继续发出真正的请求

    CORS 从具体的代码实现上来说还是比较方便的,前端不需要编写任何代码,主要是靠服务端进行配置

    CORS 需要游览器和服务器同时支持,目前几乎所有的游览器都支持该功能,IE 游览器不能低于 IE 10

    同时 CORS 也是比较推荐的一种处理跨域请求的做法,具体实现请看后文


代码实现

上述的三种解决跨域的方案中 CORS 是可以完全由后端去实现的,以下内容我就以 Spring BootSpring Cloud 为例概述如何解决跨域问题

这里我在后台启动一个 Spring Cloud 的项目,其中有个 Gateway 服务和一个 User 服务,用户服务中有一个接口 /auth/loign

在这里插入图片描述

如果仅仅看 User 服务它就是一个 Spring Boot 的项目,访问该接口的 URL 为:POST http://localhost:8868/auth/login

Spring Cloud 项目的话一般请求都是走网关的,这里正确的请求 URL 为:POST http://localhost:8860/user/auth/login

前端发送请求代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>发送请求</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/1.7.2/axios.js"></script>
</head>

<body>

    <button id="bootNpde">Spring Boot 跨域请求</button>
    <button id="cloudNode">Spring Cloud 跨域请求</button>


    <script>

        bootNpde.onclick = function () {
            const p = axios.post("http://localhost:8868/auth/login", {
                username: "admin",
                password: "admin"
            });
            console.log(p);
            p.then(result => {
                console.log(result);
                // 获取服务器端的响应数据
                const { data } = result;
                console.log(data);
            }).catch(err => {
                console.log(err);
            })
        }

        cloudNode.onclick = function () {
            const p = axios.post("http://localhost:8860/user/auth/login", {
                username: "admin",
                password: "admin"
            });
            console.log(p);
            p.then(result => {
                console.log(result);
                // 获取服务器端的响应数据
                const { data } = result;
                console.log(data);
            }).catch(err => {
                console.log(err);
            })
        }

    </script>

</body>

</html>

目前项目没有做任何的跨域处理,所以访问这两个 URL 的时候均会出现跨域错误

在这里插入图片描述

(1)Spring Boot 跨域处理

  • 方式一:在接口或者方法上通过注解 @CrossOrigin 来解决跨域问题

@CrossOrigin 注解是 Spring 框架中的一个注解,它的主要作用是处理跨域资源共享(CORS)问题

使用 @CrossOrigin 注解,可以在控制器类或具体处理方法上指定哪些来源可以访问该类或方法,从而实现对 CORS 的简化配置。当在类或方法上使用此注解时,Spring 会自动在 HTTP 响应头中添加适当的 CORS 相关头部信息,如 Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers 等,从而允许浏览器执行跨域请求

@CrossOrigin 注解支持多种配置选项,例如允许特定来源、允许所有来源、指定请求头和响应头等。例如,可以在注解中指定 origins 属性来限制可以访问的域名列表,或使用 methods 属性来指定允许的 HTTP 请求方法

代码示例:

@Slf4j
@RestController
@RequestMapping("/auth")
@Api(tags = "【登录认证】")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@CrossOrigin(origins = "*", methods = {RequestMethod.POST, RequestMethod.GET})
public class AuthController {

    private final AuthService authService;

    @PostMapping(value = "/login")
    @ApiOperation(value = "用户登录")
    //@CrossOrigin(origins = "http://localhost:5173", methods = {RequestMethod.POST, RequestMethod.GET})
    public ResponseBean<LoginResp> login(@RequestBody @Validated LoginReq req) {
        return ResponseBean.success(authService.login(req));
    }
}

测试:

在这里插入图片描述

  • 方式二:配置全局 CORS 映射

如果你希望全局地配置 CORS 策略,可以在 Spring Boot 配置类中添加一个 CORS 配置 bean

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class CorsConfig {

    @Bean
    public CorsFilter corsFilter() {

        CorsConfiguration config = new CorsConfiguration();
        // 允许向该服务器提交请求的 URI,* 表示全部允许,在 SpringMVC 中,如果设成 *,会自动转成当前请求头中的 Origin
        //config.addAllowedOrigin("*");
        // 指定允许跨域的域名
        //config.addAllowedOrigin("http://localhost:5173");
        // springboot2.4.2 中 addAllowedOrigin 不允许设置为 *,要改成使用 AllowedOriginPattern
        config.addAllowedOriginPattern("*");
        // 允许访问的头信息,* 表示全部
        config.addAllowedHeader("*");
        // 允许提交请求的方法,*表示全部允许
        config.addAllowedMethod("*");
        // 允许 cookies 跨域
        config.setAllowCredentials(true);
        // 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
        config.setMaxAge(18000L);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}

或者写成:

import org.springframework.context.annotation.Bean;
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 {

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry
                        // 映射到所有路径
                        .addMapping("/**")
                        // 允许所有域名进行跨域调用
                        //.allowedOrigins("*")
                        // springboot2.4.2 后 addAllowedOrigin 不允许设置为 *,要改成使用 AllowedOriginPattern
                        .allowedOriginPatterns("*")
                        // 允许所有请求方式跨域调用
                        .allowedMethods("*")
                        // 放行全部原始头信息
                        .allowedHeaders("*")
                        // 允许携带 Cookie 信息
                        .allowCredentials(true)
                        // 预检请求的缓存时间
                        .maxAge(18000L);
            }
        };
    }

}

测试:

在这里插入图片描述

(2)Spring Cloud 跨域处理

  • 方式一:配置全局 CORS 映射

Spring boot 配置全局 CORS 差不多,不过是在 Gateway 服务的配置类中添加一个 CORS 配置 bean

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;

/**
 * 跨域处理
 */
@Configuration
public class GlobalCorsConfiguration {

    @Bean
    public CorsWebFilter corsFilter() {
        // 配置跨域信息
        CorsConfiguration config = new CorsConfiguration();
        // 允许跨域的请求来源:允许所有域名进行跨域调用
        config.addAllowedOriginPattern("*");
        // 允许跨域的头:放行全部原始头信息
        config.addAllowedHeader("*");
        // 允许跨域的请求方式:允许所有请求方式跨域调用
        config.addAllowedMethod("*");
        // 允许携带 Cookie 信息
        config.setAllowCredentials(true);

        // 添加映射路径
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        // 任意 url 都要进行跨域配置
        source.registerCorsConfiguration("/**", config);

        return new CorsWebFilter(source);
    }

}

在这里插入图片描述

  • 方式二:在配置文件中设置 CORS 策略

Gateway 服务的配置文件 application.yml 中设置 CORS 策略

示例:

spring:
  cloud:
    gateway:
      # 全局的跨域处理
      globalcors: 
        # 解决 options 请求被拦截问题
        add-to-simple-url-handler-mapping: true 
        cors-configurations:
          '[/**]':
            allowedOrigins:  # 允许哪些网站的跨域请求
              - "http://localhost:5173"
            allowedMethods:  # 允许的跨域 ajax 的请求方式
              - "GET"
              - "POST"
              - "DELETE"
              - "PUT"
              - "OPTIONS"
            # 允许在请求中携带的头信息  
            allowedHeaders: "*"
            # 是否允许携带 cookie
            allowCredentials: true
            # 这次跨域检测的有效期
            maxAge: 36000

需要注意的问题:

对于 Spring Cloud Gateway,你可以通过全局 CORS 配置或者特定路由的 CORS 配置来设置 CORS。确保没有其他过滤器或配置在之后覆盖了你的 CORS 设置

也就是说最好只配置一个 CORS,比如你在配置文件中配置了全局跨域处理,允许 http://localhost:5173 进行跨域,那么就不要用 @CrossOrigin 在接口上设置了 CORS

例如:

@Slf4j
@RestController
@RequestMapping("/auth")
@Api(tags = "【登录认证】")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@CrossOrigin(origins = "*", methods = {RequestMethod.POST, RequestMethod.GET})
public class AuthController {

    private final AuthService authService;

    @PostMapping(value = "/login")
    @ApiOperation(value = "用户登录")
    //@CrossOrigin(origins = "http://localhost:5173", methods = {RequestMethod.POST, RequestMethod.GET})
    public ResponseBean<LoginResp> login(@RequestBody @Validated LoginReq req) {
        return ResponseBean.success(authService.login(req));
    }
}

不然就会因为响应头 Access-Control-Allow-Origin 包含多个值时,被浏览器拒绝该响应

在这里插入图片描述

在这里插入图片描述

报错信息:

Access to XMLHttpRequest at 'http://localhost:8860/user/auth/login' from origin 'http://localhost:5173' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values 'http://localhost:5173, *', but only one is allowed.

这样就导致了即使你配置了跨域,但是还是会出现跨域的问题的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/707452.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL 用户权限管理:授权、撤销、密码更新和用户删除(图文解析)

目录 前言1. 授予权限2. 撤销权限3. 查询权限4. Demo 前言 公司内部的数据库权限一般针对不同人员有不同的权限分配&#xff0c;而不都统一给一个root权限 1. 授予权限 授予用户权限的基本命令是GRANT 可以授予的权限种类很多&#xff0c;涵盖从数据库和表级别到列和存储过…

郑州申请大气污染防治乙级资质,这些材料必不可少

在郑州申请大气污染防治乙级资质时&#xff0c;以下材料是必不可少的&#xff1a; 一、企业基础资料&#xff1a; 企业法人营业执照副本&#xff1a;需清晰&#xff0c;且在有效期内[1][2]。企业章程&#xff1a;提交企业章程的扫描件或复印件&#xff0c;以展示企业的组织结构…

183.二叉树:二叉搜索树中的众数(力扣)

代码解决 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* Tre…

农资投入品系统架构:数字化农业的技术支撑与创新

在当今数字化时代&#xff0c;农业领域也在迅速迈向数字化和智能化的新阶段。农资投入品系统作为农业生产的重要支撑&#xff0c;其系统架构的设计与创新对于提高农业生产效率、保障粮食安全具有重要意义。本文将探讨农资投入品系统架构的设计原则、核心模块以及未来发展趋势。…

实例化游戏物体的实例(生成游戏物体)

一、实例1&#xff1a;实例化 1、准备工作&#xff1a;制备预制体&#xff0c;命名。如Circle 2、Create Empty&#xff0c;名字自取。如&#xff1a;CirclePrefab 3、给CirclePrefab添加Test.cs public GameObject CirclePrefab; // 预制体变量&#xff0c;用于存储Circle预…

湖南安全技术职业学院校长邓德艾一行到我中心考察交流

6月4日上午&#xff0c;湖南安全技术职业学院校长邓德艾一行莅临方班网安人才教育服务中心交流研讨&#xff0c;一方面是访企拓岗&#xff0c;另一方面是针对产教融合、政校企合作人才培养展开深入研讨。为将本次研讨进行得更深入全面&#xff0c;方班网安人才教育服务中心邀请…

C++17并行算法与HIPSTDPAR

C17 parallel algorithms and HIPSTDPAR — ROCm Blogs (amd.com) C17标准在原有的C标准库中引入了并行算法的概念。像std::transform这样的并行版本算法保持了与常规串行版本相同的签名&#xff0c;只是增加了一个额外的参数来指定使用的执行策略。这种灵活性使得已经使用C标准…

AI数字人的开源解决方案

目前&#xff0c;国内外已经涌现出一些优秀的数字人开源解决方案&#xff0c;这些解决方案为开发者提供了构建数字人应用的工具和基础设施。以下是一些比较知名的数字人开源解决方案。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1…

Sklearn中逻辑回归建模

分类模型的评估 回归模型的评估方法&#xff0c;主要有均方误差MSE&#xff0c;R方得分等指标&#xff0c;在分类模型中&#xff0c;我们主要应用的是准确率这个评估指标&#xff0c;除此之外&#xff0c;常用的二分类模型的模型评估指标还有召回率&#xff08;Recall&#xff…

振弦采集仪在隧道工程中的安全监测与控制研究

振弦采集仪在隧道工程中的安全监测与控制研究 隧道工程的安全监测与控制是保障隧道施工和运营安全的重要工作。隧道工程常面临的问题包括地层变形、地下水位变化、地震影响等&#xff0c;这些问题对隧道结构的安全性和使用寿命有着重要影响。因此&#xff0c;隧道工程中的安全…

JVM性能优化案例:减少对象频繁创建

JVM性能优化案例&#xff1a;减少对象频繁创建 案例背景 某金融应用系统在处理大量并发交易时&#xff0c;响应时间过长&#xff0c;并且有时出现内存溢出&#xff08;OutOfMemoryError&#xff09;的问题。经过分析&#xff0c;发现问题主要出在频繁的对象创建和较差的内存管…

OpenCV查找图像中的轮廓并且展示

1、查找轮廓随机用不同的颜色画出 import cv2 import numpy as npdef get_contour_colors(num_contours):# 定义颜色表 (BGR 格式)colors [(255, 0, 0),(255, 50, 0),(255, 100, 0),(255, 150, 0),(255, 200, 0),(255, 255, 0),(200, 255, 0),(150, 255, 0),(100, 255, 0),(5…

Linux常⽤服务器构建-ssh和scp

目录 1.ssh <1>ssh介绍 <2>安装ssh A.安装ssh服务器 B.远程登陆 <3>使⽤ssh连接服务器 2.scp 本地⽂件复制到远程&#xff1a; 本地⽬录复制到远程&#xff1a; 远程⽂件复制到本地&#xff1a; 远程⽬录复制到本地&#xff1a; 1.ssh <1>…

【LLM之RAG】Self-RAG论文阅读笔记

研究背景 尽管大型语言模型&#xff08;LLM&#xff09;展示出了显著的能力&#xff0c;但它们在生成回答时经常包含事实错误&#xff0c;因为它们仅依赖于封装在模型中的参数知识。增强型检索生成&#xff08;Retrieval-Augmented Generation, RAG&#xff09;是一种方法&…

leetcode695 岛屿的最大面积

题目 给你一个大小为 m x n 的二进制矩阵 grid 。 岛屿 是由一些相邻的 1 (代表土地) 构成的组合&#xff0c;这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0&#xff08;代表水&#xff09;包围着。 岛屿的面积是岛上值…

ubuntu18.04离线源制作

给客户部署有时需要纯内网环境&#xff0c;那这样就连不了网络。 一些包就下载不下来&#xff0c;而大家都知道用deb离线安装是非常麻烦的&#xff0c;各种依赖让你装不出来。 这里教大家打包源。 我准备2台机器&#xff0c;42和41 42可以联网&#xff0c;41不能联网。我想在…

在AI云原生时代应该如何应对复杂的算力环境

引言 随着在2019年ChatGPT4的爆火,AI这个之前常常被人觉得非常高深的技术渐渐的被越来越多的人们所了解,越来越多的公司、组织和开发者开始投入AI的使用和开发中来.随着AI和LLM的火热,算力资源也变的越来越紧缺,所以如何高效的管理和使用算力资源也变成了必须要面对的问题。 …

2024全站焕新,重塑3D轻量体验!

3D模型当前应用广泛&#xff0c;正以惊人的速度实现数据增长&#xff0c;轻量化需求随之增多。老子云团队一直在探索如何借助自研轻量化技术的能力&#xff0c;打破用户模型处理思维惯性&#xff0c;构建更高效、实用、简单的体验范式&#xff0c;来帮助用户解决3D素材数据处理…

教学辅助系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;教师管理&#xff0c;作业管理&#xff0c;学生管理&#xff0c;管理员管理&#xff0c;作业提交管理&#xff0c;教学视频管理 教室账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0…

L1-098 再进去几个人

L1-098 再进去几个人 分数 5 全屏浏览 切换布局 作者 陈越 单位 浙江大学 数学家、生物学家和物理学家坐在街头咖啡屋里&#xff0c;看着人们从街对面的一间房子走进走出。他们先看到两个人进去。时光流逝。他们又看到三个人出来。 物理学家:“测量不够准确。” 生物学家:“…