SpringBoot3解决跨域请求问题(同源策略、JSONP、CORS策略)(Access-Control-Allow-Origin)(2025详细教程)
目录
浏览器跨域请求问题。
浏览器同源策略。
第三方API调用。
前后端分离项目。
一、JSONP。(dataType:'jsonp')
(1)代码示例。
<1>前端ajax04.jsp页面。(发起Ajax请求)
<2>后端springboot接口。(/hello)(返回JSONPObject对象)
(2)使用"JSONP"时注意事项。
Cross-Origin Resource Sharing策略。(跨域资源共享)
(1)基本介绍。
(2)基本工作原理。(Access-Control-Allow-Origin)
二、注解@CorssMapping。(用在控制器类或方法)
(1)代码示例。
<1>前端ajax.jsp页面。(发起Ajax请求)
<2>后端控制器类。("/employee/selectById")(注解@CrossOrigin)
三、SpringBoot3中自定义跨域请求与配置。(处理跨域拦截)
(1)实现接口WebMvcConfigurer。(配置类@Configuration)
<1>重写addCorsMapping()方法。
<2>Vue3页面中的Ajax请求。(async)
test02.vue页面代码。
test02.vue页面路由配置。
封装request、response的js文件。
<3>点击按钮,触发绑定事件(请求),页面渲染效果。
(2)配置CorsFilter对象并加入IoC容器(@Bean)。(全局的CORS配置)
<1>后端配置类。(CorsConfig跨资源共享配置类)(返回CorsFilter跨资源共享拦截器对象)
-
浏览器跨域请求问题。
浏览器同源策略。
- 浏览器的同源策略是一种重要的安全机制,用于限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。
- 浏览器同源策略的主要目的:防止恶意网站窃取用户的敏感信息。如果没有同源策略,恶意网站可以通过脚本读取用户访问过的其他网站的内容、发送请求等,从而获取用户的登录凭证、个人信息等。
第三方API调用。
- 当网页需要调用第三方提供的 API 时,由于第三方 API 的域名与当前网页的域名不同,会产生跨域请求。如网页中集成了地图服务,需要调用百度地图的API来获取地图数据和相关功能,而百度地图API的域名与网页所在域名不同,就会触发跨域请求。
前后端分离项目。
- 通常在Web开发中,很多项目采用前后端分离的架构。前端代码部署在一个域名下,而后端 API 服务部署在另一个域名下。此时当浏览器从前端页面向后端API发送请求时,就会发生跨域请求。
- 前端工程部署在本地:http://localhost:5173/。
- 后端工程部署在本地:http://localhost:9090/。
- 因浏览器的同源策略导致前端发起的请求被阻止。
一、JSONP。(dataType:'jsonp')
- 用于解决跨域的Ajax请求的一种方式。(不常用)
(1)代码示例。
<1>前端ajax04.jsp页面。(发起Ajax请求)
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>ajax请求</title> <%--引入jquery--%> <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script> </head> <body> <button onclick="myFunction()">调用接口:/hello</button> <script> function myFunction() { $.ajax({ url:"http://localhost:9090/hello", type:"GET", dataType:"jsonp", success:function (data){ console.log(data) } }) } </script> </body> </html>
<2>后端springboot接口。(/hello)(返回JSONPObject对象)
package com.hyl.controller; import com.fasterxml.jackson.databind.util.JSONPObject; import com.hyl.common.Result; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; //表示对外提供可访问接口的类 @RestController public class WebController { //测试接口 @GetMapping("/hello") public JSONPObject hello(String callback){ return new JSONPObject(callback,Result.success("hello")); } }
package com.hyl.common; /** * 统一后端返回的数据结果类型 */ public class Result { //请求状态码 private String code; //成功或错误信息 private String msg; //返回数据结果 private Object data; //常用静态方法 //请求成功且无数据返回 public static Result success(){ Result result = new Result(); result.setCode("200"); result.setMsg("请求成功"); return result; } //请求成功且有数据返回。 public static Result success(Object data) { //直接调用静态方法设置状态码、请求成功信息 Result result = Result.success(); result.setData(data); return result; } //请求失败且无数据返回 public static Result error(){ Result result = new Result(); result.setCode("500"); result.setMsg("系统出错了!"); return result; } //请求失败且无数据返回。适配自定义异常(传递code、msg) public static Result error(String code,String msg){ Result result = new Result(); result.setCode(code); result.setMsg(msg); return result; } //getter、setter方法 public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } }
(2)使用"JSONP"时注意事项。
- 许多浏览器不兼容问题。(该方法不常用)
- 后端必须返回JSONPObject对象。大量使用耦合度高。
- 综上:对于"JSONP"方式使用大概了解与能够简单使用就行,不需要深度学习。
-
Cross-Origin Resource Sharing策略。(跨域资源共享)
(1)基本介绍。
- CORS是一种基于 HTTP头的机制,它允许 Web应用程序从不同源(域名、协议、端口)请求和访问资源,克服了浏览器的同源策略限制。
(2)基本工作原理。(Access-Control-Allow-Origin)
- 1、浏览器在发送跨域请求时,会先发送一个预检请求,以询问服务器是否允许该跨域请求。
- 2、服务器收到预检请求后,会检查请求头中的相关信息,如Origin(表示请求的源),并根据自身的CORS配置返回相应的响应头。
- 3、如果服务器允许该请求,会在响应头中包含Access-Control-Allow-Origin等字段,告知浏览器该请求的源是被允许的。浏览器接收到允许跨域的响应后,才会发送实际的请求。
- 如下面控制台报错未在响应头中告诉浏览器所需字段。就受到了浏览器同源策略限制。
二、注解@CorssMapping。(用在控制器类或方法)
(1)代码示例。
<1>前端ajax.jsp页面。(发起Ajax请求)
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>ajax请求</title> <%--引入jquery--%> <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script> </head> <body> <button onclick="myFunction()">调用接口:/employee/selectById/{id}</button> <script> let id = 1; //测试请求id为1的员工信息 function myFunction() { $.ajax({ url:"http://localhost:9090/employee/selectById/"+ id, type:"GET", dataType:"json", success:function (data){ alert('请求状态码:'+data.code+'\n请求消息:'+data.msg+'\n请求数据:'+JSON.stringify(data.data)) console.log("根据Id获取员工信息:"+JSON.stringify(data.data)) } }) } </script> </body> </html>
<2>后端控制器类。("/employee/selectById")(注解@CrossOrigin)
package com.hyl.controller; import com.hyl.common.Result; import com.hyl.entity.Employee; import com.hyl.service.EmployeeService; import jakarta.annotation.Resource; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/employee") public class EmployeeController { @Resource private EmployeeService employeeService; /** *根据id查询员工 */ @GetMapping("/selectById/{id}") @CrossOrigin("http://localhost:8080/") public Result selectById(@PathVariable Integer id){ Employee employee = employeeService.selectById(id); return Result.success(employee); } }
- 请求成功。结果如下。
三、SpringBoot3中自定义跨域请求与配置。(处理跨域拦截)
(1)实现接口WebMvcConfigurer。(配置类@Configuration)
<1>重写addCorsMapping()方法。
- 后端配置类。
package com.hyl.common; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.*; @Configuration public class CorsConfig02 implements WebMvcConfigurer { // 处理跨域请求 @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") /*.allowedOrigins("*") //允许所有源的跨域请求*/ /*.allowedOriginPatterns("http://localhost:5173/") //指定允许的源模式*/ .allowedOriginPatterns("http://localhost*") //指定允许的源模式 .allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的方法 .allowedHeaders("*") // 允许的请求头 .allowCredentials(true) // 允许携带凭证 .maxAge(3600); // 预检请求的缓存时间 } }
<2>Vue3页面中的Ajax请求。(async)
test02.vue页面代码。
<template> <div style="margin: 30px"> <h3>测试Ajax请求后端接口"/selectById/{id}"</h3> <el-button type="success" @click="testAjax(data.id)">发起请求 获取数据</el-button> <div style="margin: 15px;"><span>请求数据:{{data.employee}}</span></div> <div style="margin: 15px;"><span>请求状态码:{{data.code}}</span></div> </div> </template> <script setup> import {reactive} from "vue"; import request from "@/utils/request.js"; const data = reactive({ employee:null, id:1, //假定测试请求id=1的员工信息 code:null, }) const testAjax = async (id) =>{ try { const response = await request.get("/employee/selectById/" + id); data.employee = response.data data.code = response.code } catch (e) { console.error(e) } } </script> <style scoped> </style>
test02.vue页面路由配置。
import { createRouter, createWebHistory } from 'vue-router' const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ {path:'/',redirect:'/manager/home'}, {path:'/manager',meta:{ title:'父级页面'},component: () => import('../views/Manager.vue'),children:[ {path: 'home', name: 'home', meta:{ title:'主页'}, component: () => import('../views/Home.vue')}, // url:/manager/home {path: 'test', name: 'test', meta:{ title:'测试数据页01'}, component: () => import('../views/Test.vue')}, // url:/manager/test {path: 'demo', name: 'demo', meta:{ title:'测试数据页02'}, component: () => import('../views/Demo.vue')}, // url:/manager/demo {path: 'data', name: 'data', meta:{ title:'数据展示页面'}, component: () => import('../views/Data.vue')}, // url:/manager/data {path: 'employee', name: 'employee', meta:{ title:'员工信息页面'}, component: () => import('../views/Employee.vue')}, // url:/manager/employee {path: 'test02', name: 'test02', meta:{ title:'测试Ajax请求'}, component: () => import('../views/Test02.vue')}, // url:/manager/test02 ]}, {path: '/login', name: 'login', meta:{ title:'登录页面'}, component: () => import('../views/Login.vue')}, {path: '/register', name: 'register', meta:{ title:'欢迎注册'}, component: () => import('../views/Register.vue')}, {path: '/404', name: 'NotFound', meta:{ title:'404找不到页面'}, component: () => import('../views/404.vue')}, {path:'/:pathMatch(.*)',redirect:'/404'} ], }) router.beforeEach((to,from,next)=>{ //设置即将跳转的路由页面的网页标题 document.title=to.meta.title next() //必须调用的方法 }) export default router
封装request、response的js文件。
import { ElMessage } from 'element-plus' import axios from "axios"; const request = axios.create({ //设置后台请求地址 baseURL: 'http://localhost:9090', timeout: 30000 // 后台接口超时时间设置 }) // request 拦截器(数据请求) // 可以自请求发送前对请求做一些处理 request.interceptors.request.use(config => { //设置统一的数据传输格式json、数据传输编码utf-8 config.headers['Content-Type'] = 'application/json;charset=utf-8'; return config }, error => { return Promise.reject(error) }); // response 拦截器 // 可以在接口响应后统一处理结果 request.interceptors.response.use( response => { //响应对象response中提取实际数据部分,存储在变量res中 let res = response.data; // 兼容服务端返回的字符串数据 //如果res是字符串且不为空字符串,则使用JSON.parse方法将其解析为JavaScript对象; //如果 res 为空字符串,则保持原样。 if (typeof res === 'string') { res = res ? JSON.parse(res) : res } return res; }, error => { if(error.response.status === 404){ //404 状态码表示请求的资源未找到,通常意味着请求的接口不存在 ElMessage.error('未找到请求接口') }else if(error.response.status === 500){ //500:之前后端设置的全局系统异常处理捕获 //500 状态码表示服务器内部错误,通常是由于后端代码出现异常 ElMessage.error('系统异常,请查看后端控制台报错') }else{ //其它情况统一打印错误信息 console.error(error.message) } //将错误继续抛出,以便后续的代码可以继续处理该错误 return Promise.reject(error) } ) export default request
<3>点击按钮,触发绑定事件(请求),页面渲染效果。
- 未点击按钮时。
- 点击按钮。请求指定接口并获取数据渲染页面。
(2)配置CorsFilter对象并加入IoC容器(@Bean)。(全局的CORS配置)
<1>后端配置类。(CorsConfig跨资源共享配置类)(返回CorsFilter跨资源共享拦截器对象)
package com.hyl.common; 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 让该配置类注入到Spring容器中并能够扫描到其下类下注解 */ @Configuration public class CorsConfig { /** * 创建并注册CorsFilter bean,用于处理跨域请求 * @return CorsFilter实例,Spring会在请求处理过程中使用该过滤器处理跨域请求 * @Bean 注解会让该方法的返回值注入到Spring容器中 */ @Bean public CorsFilter corsFilter() { // 创建一个基于URL的跨域配置源对象,用于存储和管理不同URL路径的跨域配置 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); // 创建一个跨域配置对象,用于设置具体跨域规则 CorsConfiguration corsConfiguration = new CorsConfiguration(); // 设置允许访问的源地址,这里使用通配符"*",表示允许所有源地址访问 corsConfiguration.addAllowedOrigin("*"); // 设置允许的请求头,"*"表示允许所有请求头,即前端可以在请求中携带任意请求头 corsConfiguration.addAllowedHeader("*"); // 设置允许的请求方法,"*"表示允许所有的HTTP请求方法,如GET、POST、PUT、DELETE等 corsConfiguration.addAllowedMethod("*"); // 将跨域配置应用到所有的接口路径上,"/**"表示匹配所有路径 source.registerCorsConfiguration("/**", corsConfiguration); // 创建并返回CorsFilter实例,传入配置源对象,Spring会使用该过滤器处理跨域请求 return new CorsFilter(source); } }
- 前端Vue页面(localhost:5173)请求后端springboot服务器(localhost:9090)。页面渲染效果如下。