跨域3种解决方式

142 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

什么是跨域?

跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript实施的安全限制。

同源策略限制以下行为:

  • Cookie、LocalStorage 和 IndexDB 无法读取
  • DOM 和 JS 对象无法获取
  • Ajax请求发送不出去

解决跨域的方法(3种)

  • JSONP
  • 后端解决( CROS)
  • 前端解决(前后端--Vue等)
  1. JSONP
  • 普通请求值 XHR,希望得到服务端返回的 content-type 一般是 json
  • JSONP 发出的是 script 请求,希望得到的返回是 js 脚本
JSONP原理:
发送 ajax 请求的时候,设置dataType:"jsonp",将使用 JSONP 方式调``用函数,
函数的 url 变为myurl?callback=e5bbttt的形式,
根据一个临时方法名,后端会根据callback的值返回一个 js 脚本
JSONP弊端:
1. 需要服务器改动代码
2. 只支持 GET 请求
3. 发送的不是 xhr 请求
4. 不安全
  1. CROS(用于后端解决跨域问题)
  • SpringWebConfiguration.java
@Configuration
@EnableWebMvc
public class SpringWebConfiguration implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")// 添加映射路径
                .allowedHeaders("*")// 放行哪些原始请求头部信息
                .exposedHeaders("*")// 暴露哪些头部信息
                .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE", "TRACE", "HEAD", "PATCH")// 放行哪些请求方式
                .allowCredentials(true)// 是否发送 Cookie
                .maxAge(3600)// 最大时间
                .exposedHeaders("Authorization")
                .allowedOriginPatterns("*");
                // 指定明确地址
//                .allowedOriginPatterns("http://127.0.0.1:8080")
//                .allowedOriginPatterns("http://127.0.0.1:8082");
    }

}

如说使用发的是前后端开发(例如VUE等),为了方便开发不用每次写全域名还需要在axios中作如下配置

  • request.ts
import axios, {AxiosRequestConfig, AxiosResponse} from 'axios'
import {ElMessage, ElNotification} from 'element-plus'
import {useRouter} from 'vue-router'
import * as nProgress from 'nprogress'
import {useMainStore} from '../store'

const router = useRouter()

axios.create({
    baseURL: 'http://127.0.0.1:8080',
    timeout: 5000,
    withCredentials: true
})

axios.defaults.headers.post['Content-Type'] = 'application/json;charset=utf-8';

axios.interceptors.request.use(async (config: AxiosRequestConfig) => {
    nProgress.start()
    if (useMainStore().getAuthorization && localStorage.getItem('Authorization')) {
        if (config.headers) {
            config.headers.Authorization = useMainStore().getAuthorization
        }
    }
    return config;
}, ((error: any) => {
    ElNotification.error('请求错误!')
    return Promise.reject(error);
}))

axios.interceptors.response.use(async (response: AxiosResponse) => {
    nProgress.done()
    switch (response.status as number) {
        case 200 || 201:
            return response;
        case 401:
            ElMessage.warning('您未登录')
            await router.push('/login')
            break
        case 403:
            ElMessage.warning('无权访问')
            break
        case 500:
            ElMessage.error('服务器异常')
            break
        default:
            ElNotification.error('未知异常')
    }

}, ((error: any) => {
        ElNotification.error('响应错误!')
        return Promise.reject(error);
    }
))

export default axios
  1. Proxy(前端代理方式)
  • 使用vue时:vue.config.js
module.exports = {
  lintOnSave: false,
  devServer: {
    // 前端地址
    host: '127.0.0.1', 
    // 前端端口
    port: 3000, 
    // 设置代理
    proxy: {
      '/api': {
          //远程服务器地址
         target: 'http://127.0.0.1:8080',  
         changeOrigin: true,
         pathRewrite: {
             '^/api': ''
         }
      }
    }
  }
}
  • 使用vite时:vite.config.ts
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import * as path from 'path'

export default defineConfig({
    plugins: [
        vue()
    ],
    resolve: {
        alias: {
            '@': path.resolve(__dirname, 'src'),
            '@views': path.resolve(__dirname, 'src/views'),
            '@layout': path.resolve(__dirname, 'src/layout'),
            '@components': path.resolve(__dirname, 'src/components')
        }
    },
    server: {
        host: '127.0.0.1',
        port: 3000,
        cors: true,
        open: false,
        proxy: {
            '/api': {
                target: 'http://127.0.0.1:8080',
                changeOrigin: true,
                rewrite: (path: string) => path.replace(/^/api/, '')
            }
        }
    }
})

test.vue

<template>
  <el-button type="success" @click="testProxy">测试</el-button>
</template>

<script lang="ts" setup>
import axios from './utils/request'

const testProxy = async () => {
    const { data } = await axios({
      method: 'GET',
      url: '/api/test'
    })
    switch( data.code as number ){
        case 200 || 201:
            ElMessage.warning('请求成功')
            break
        case 500:
            ElMessage.warning('服务器异常')
            break
        default:
            ElMessage.warning('未知异常')
    }
}
</script>

<style scoped lang="scss">

</style>