这真是一个老生常谈的问题
起因是一个工作一年的粉丝出现了跨域问题
跨域答疑
简单描述一下吧:
A是后端开发
B是前端开发
A发现B的后台页面调登录接口跨域了,然后A在后端做了CORS,B在前端设置了前端代理
理论上来说这两个其中一个正常生效,都是没问题的,都不可能出现跨域问题
经过我进一步的盘问,发现他们是在线上服务器环境出现的跨域问题
那就有意思了,线上环境,不都通过nginx反向代理了吗,为啥还会有跨域问题
叫A截了一张图

通过这张图我就发现一个问题
现在前端在8082端口,调接口怎么会调到8088端口去呢
所以敢肯定,前端把接口地址写死了
前端是直接通过axios请求后端接口,没有被nginx匹配到,然后后端设置的CORS要么没有生效,要么写错了,导致出现的跨域
所以解决问题很简单,前端改下axios的baseURL配置,把它去掉或者写服务器的8082地址都可以解决问题

为什么会跨域
跨域问题解决了,但是有没有想过为什么会跨域?
这节课一次性搞懂为什么会跨域以及常见的解决办法
首先跨域是浏览器行为,是浏览器认为不安全进行拦截的,不信你通过curl和apifox去请求,肯定不会有跨域的
首先浏览器有一个策略叫 “同源策略”,它是浏览器最核心也最基本的安全功能
当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不一同,即跨域。
比如上面 前端页面在 8082,去请求8088的后端,那肯定就跨域了。
如何解决跨域
解决跨域有两种方法,一种是代理,另一种是CORS
CORS解决跨域
CORS很简单,就是由后端告诉浏览器,这个请求我授权了是安全的,你放行吧
CORS的设置也很简单,在响应的响应头上加上对应的响应头即可
Access-Control-Allow-Origin: * // 表明接受什么域名
Access-Control-Allow-Credentials: true // 是否允许发送cookie
Access-Control-Expose-Headers: XXX // 是否暴露其他headers值
例如gin中去解决跨域
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func Cors() gin.HandlerFunc {
return func(c *gin.Context) {
method := c.Request.Method
if c.Request.Header.Get("origin") != "" {
c.Header("Access-Control-Allow-Origin", "*") // 可将将 * 替换为指定的域名
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
c.Header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization")
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type")
c.Header("Access-Control-Allow-Credentials", "true")
}
if method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
}
c.Next()
}
}
func Index(c *gin.Context) {
c.JSON(200, gin.H{
"code": 0,
"msg": "成功",
"data": gin.H{},
})
return
}
func main() {
r := gin.Default()
r.GET("/api/no_cors", Index)
r.POST("/api/no_cors", Index)
r.GET("/api/cors", Cors(), Index)
r.POST("/api/cors", Cors(), Index)
r.Run(":8080")
}
代理
从同源策略层面,让不同源的请求变成同源的请求

这种方案是目前最主流的跨域解决方案,它分为两类,一个是开发环境,一个是生产环境
开发环境解决跨域
以vue3为例,vite提供了代理功能
import {fileURLToPath, URL} from 'node:url'
import {defineConfig, loadEnv} from 'vite'
import vue from '@vitejs/plugin-vue'
import type {ImportMetaEnv} from "./env";
// https://vitejs.dev/config/
export default defineConfig(({mode}) => {
let env: Record<keyof ImportMetaEnv, string> = loadEnv(mode, process.cwd())
const serverUrl = env.VITE_SERVER_URL
const wsUrl = serverUrl.replace("http", "ws")
return {
plugins: [
vue(),
],
envDir: "./",
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
server: {
host: "0.0.0.0",
port: 80,
proxy: {
"/api": {
target: serverUrl,
changeOrigin: true,
}
}
}
}
})
凡是使用代理的情况,axios请求的后端路径就不能写死了
因为一旦写死了,代理就捕获不到了,相当于还是前端直接请求后端接口,肯定会跨域的
生产环境解决跨域
使用nginx的反向代理
server {
listen 80;
server_name blog.fengfengzhidao.com;
location / {
try_files $uri $uri/ /index.html;
root /opt/gvb/web/dist;
index index.html index.htm;
}
location /api/ {
# rewrite ^/(api/.*) /$1 break;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_pass http://127.0.0.1:8082/api/;
}
location /uploads/ {
alias /opt/gvb/server/uploads/;
}
access_log /opt/gvb/access.log;
error_log /opt/gvb/error.log;
}