跨域请求实现方案
跨域(Cross-Origin)指的是从一个源(Origin)向另一个不同源发起的 HTTP 请求。根据浏览器的同源策略(Same-Origin Policy),一个源只能访问同源的资源,跨域访问资源时会受到限制。理解跨域需要了解几个概念:
同源策略
同源策略是一种浏览器安全机制,用于防止一个源的恶意脚本访问另一个源的敏感数据。一个源由以下三部分组成:
-
协议(Scheme):如 http
、https
。
-
域名(Host):如 example.com
。
-
端口(Port):如 80
、443
。
两个 URL 必须同时满足协议、域名和端口相同,才被认为是同源。
什么是跨域
跨域指的是协议、域名或端口任意一个不同的情况下,从一个网页向另一个不同源的服务器发起的 HTTP 请求。例如:
-
从 http://example.com
向 https://example.com
发请求(协议不同)。
-
从 http://example.com
向 http://api.example.com
发请求(域名不同)。
-
从 http://example.com:80
向 http://example.com:8080
发请求(端口不同)。
常见的跨域请求
-
AJAX 请求:前端 JavaScript 发起的异步 HTTP 请求。
-
Web Fonts:在 CSS 中加载字体文件。
-
WebGL 纹理:在 WebGL 上加载图像。
-
CSS 样式表:在 CSS 中使用 @import
加载外部样式表。
-
JavaScript 文件:使用 <script>
标签加载外部 JavaScript 文件。
-
图片:使用 <img>
标签加载外部图片。
解决跨域问题的方法
CORS(Cross-Origin Resource Sharing)
CORS 是一种机制,允许服务器指示浏览器允许来自不同源的请求。服务器通过设置特定的 HTTP 头来实现这一点。
示例
Access-Control-Allow-Origin: http://example.com Access-Control-Allow-Methods: GET, POST, PUT, DELETE Access-Control-Allow-Headers: Content-Type Access-Control-Allow-Credentials: true
|
反向代理
可以使用 nginx 对后端服务器进行反向代理,让前端所有的请求都请求到 nginx 上,然后 nginx 进行请求的路由转发,这样不但实现了跨域请求,还可以进一步配置 nginx 以实现负载均衡。
跨域案例
一个简单的登录模块Demo,使用Spring的CORS实现跨域
首先我们编写一个前端页面,用于发送登录请求
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Login Form</title> <style> body { display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #f5f5f5; } .login-container { background-color: white; padding: 20px; border-radius: 5px; box-shadow: 0 0 10px rgba(0,0,0,0.1); } .login-container h2 { margin-bottom: 20px; } .login-container input { width: 100%; padding: 10px; margin: 10px 0; border: 1px solid #ddd; border-radius: 5px; } .login-container button { width: 100%; padding: 10px; background-color: #007BFF; border: none; color: white; border-radius: 5px; cursor: pointer; } .login-container button:hover { background-color: #0056b3; } </style> </head> <body> <div class="login-container"> <h2>Login</h2> <form id="loginForm"> <input type="text" id="username" name="username" placeholder="Username" required> <input type="password" id="password" name="password" placeholder="Password" required> <button type="submit">Login</button> </form> </div>
<script> document.getElementById('loginForm').addEventListener('submit', function(event) { event.preventDefault();
var username = document.getElementById('username').value; var password = document.getElementById('password').value;
var data = JSON.stringify({ username: username, password: password });
fetch('http://localhost:8090/login', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: data }) .then(response => { if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); }) .then(data => { console.log('Success:', data); }) .catch((error) => { console.error('Error:', error); }); }); </script> </body> </html>
|
现在,我们来编写后端代码,后端使用的是SpringBoot
登录接口代码如下:
@Slf4j @RestController @RequestMapping("/login") public class controller { @PostMapping public Result<UserDTO> login(@RequestBody UserDTO userDTO) { log.info("username:{},password:{}", userDTO.getUsername(), userDTO.getPassword()); if (userDTO.getUsername().equals("admin") && userDTO.getPassword().equals("admin")) return Results.success(userDTO); else return Results.failure(userDTO); } }
|
在没有设置允许跨域请求时,前端发送登录请求,会收到如下响应:
现在,我们通过实现 WebMvcConfigurer 接口,重写其 addCorsMappings() 方法,添加允许发送跨域请求的前端地址:
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:63342") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true); } }
|
重新启动后端服务,然后在前端发送登录请求,可以发现,已经实现了跨域请求: