기술나눔

프런트엔드 크로스 도메인에 대한 철저한 이해

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

 

목차

1브라우저의 동일 출처 정책

1.1 동일 출처 정책 개요

1.2 원산지란 무엇입니까?

2. 도메인 간 거래에 대한 제한 사항은 무엇입니까?

2.1 DOM 액세스 제한

2.2 쿠키 접근 제한

2.3 데이터 획득을 위한 Ajax 제한

주의할 점 3가지

4CORS는 Ajax 도메인 간 문제를 해결합니다.

4.1CORS 개요

4.2CORS는 간단한 도메인 간 요청을 해결합니다.

4.3 단순 요청과 복잡한 요청

4.4CORS는 복잡한 도메인 간 요청을 해결합니다.

4.5 cors 라이브러리를 사용하여 구성을 빠르게 완료

5JSONP는 도메인 간 문제를 해결합니다.

6교차 도메인 문제를 해결하기 위해 프록시 구성

6.1 프록시 서버 직접 구성

6.2 Nginx를 사용하여 프록시 서버 구축

6.3 스캐폴딩을 활용한 서버 구축


코드 주소 git clone https://gitee.com/childe-jia/cross-domain-test.git

1브라우저의 동일 출처 정책

1.1 동일 출처 정책 개요


동일 출처 정책은 리소스 보안을 보장하기 위해 브라우저가 따르는 정책입니다. 이 정책은 리소스에 대한 액세스에 일부 제한을 적용합니다.
W3C의 동일 출처 정책에 대한 설명:동일 원산지 정책

1.2 원산지란 무엇입니까?


소스 구성요소 1개

이미지.png


2아래 표에서는 마지막 행의 두 소스만 출처가 동일합니다.

소스 1

소스 2

동종인가요?

http://www.xyz.com/home

https://www.xyz.com/home

⛔비균질️

http://www.xyz.com/home

http://mail.xyz.com/home

⛔비균질

http://www.xyz.com:8080/홈

http://www.xyz.com:8090/홈

⛔비균질

http://www.xyz.com:8080/홈

http://www.xyz.com:8080/search

✅같은 유래︎

 

3 원산지 요청

이미지.png


4 원본이 아닌 요청

이미지.png


5 요약: "소스"가 "대상 소스"와 일치하지 않는 경우 "비소스"를 의미하며 "헤테로소스" 또는 "크로스 도메인"이라고도 합니다.

2. 도메인 간 거래에 대한 제한 사항은 무엇입니까?


예를 들어, "원본이 동일하지 않은" "소스 A"와 "소스 B"라는 두 개의 소스가 있는 경우 브라우저에는 다음과 같은 제한 사항이 적용됩니다.

2.1 DOM 액세스 제한

"소스 A"의 스크립트는 "소스 B"의 DOM에 액세스할 수 없습니다.

  1. <!-- <iframe id="framePage" src="./demo.html"></iframe> -->
  2. <iframe id="framePage" src="https://www.baidu.com"></iframe>
  3. <script type="text/javascript" >
  4. function showDOM(){
  5. const framePage = document.getElementById('framePage')
  6. console.log(framePage.contentWindow.document) //同源的可以获取,非同源的无法获取
  7. }
  8. </script>

2.2 쿠키 접근 제한

"소스 A"는 "소스 B"의 쿠키에 액세스할 수 없습니다.

  1. <iframe id="baidu" src="http://www.baidu.com" width="500" height="300"></iframe>
  2. <script type="text/javascript" >
  3. // 访问的是当前源的cookie,并不是baidu的cookie
  4. console.log(document.cookie)
  5. </script>

2.3 데이터 획득을 위한 Ajax 제한

"소스 A"는 "소스 B"에 요청을 보낼 수 있지만 "소스 B"로부터 응답 데이터를 얻을 수는 없습니다.

  1. const url = 'https://www.toutiao.com/hot-event/hot-board/?origin=toutiao_pc'
  2. let result = await fetch(url)
  3. let data = await result.json();
  4. console.log(data)

참고: 위의 제한 사항 중 Ajax 데이터 수집에 대한 브라우저의 제한 사항이 가장 큰 영향을 미치며 실제 개발에서 자주 발생합니다.

주의할 점 3가지

  • 1교차 도메인 제한은 브라우저 측에만 존재하며, 서버 측에는 교차 도메인 제한이 없습니다.
  • 2 크로스 도메인이더라도 Ajax 요청은 정상적으로 발행 가능하지만, 응답 데이터는 개발자에게 넘겨지지 않습니다.
  • 삼<link> , <script>、<img>...... 这些标签发出的请求也可能跨域,只不过浏览器对标签跨域不做严格限制,对开发几乎无影响

이미지.png

4CORS는 Ajax 도메인 간 문제를 해결합니다.

4.1CORS 개요

CORS의 전체 이름: Cross-Origin Resource Sharing(Cross-Origin Resource Sharing)은 도메인 간 요청의 브라우저 확인을 제어하는 ​​데 사용되는 사양 집합이며, 서버는 CORS 사양을 따르고 브라우저 확인을 제어하기 위해 특정 응답 헤더를 추가합니다. 일반적인 규칙은 다음과 같습니다.
●서버가 도메인 간 요청을 명시적으로 거부하거나 이를 표시하지 않고 브라우저가 확인에 실패합니다.
●서버는 도메인 간 요청이 허용되고 브라우저 확인이 통과되었음을 명확하게 나타냅니다.
참고: CORS를 사용하여 도메인 간 문제를 해결하는 것은 가장 전통적인 방법이며 서버가 "소유"되어야 합니다.


4.2CORS는 간단한 도메인 간 요청을 해결합니다.

전반적인 아이디어: 서버가 응답할 때 Access-Control-Allow-Origin 응답 헤더를 추가하여 특정 소스가 도메인 간 요청을 시작할 수 있음을 명확하게 표현한 다음 확인 중에 브라우저가 이를 직접 전달합니다.

이미지.png

서버측 핵심 코드(Express 프레임워크를 예로 들어):

  1. // 处理跨域中间件
  2. function corsMiddleWare(req,res,next){
  3. // 允许 http://127.0.0.1:5500 这个源发起跨域请求
  4. // res.setHeader('Access-Control-Allow-Origin','http://127.0.0.1:5500')
  5. // 允许所有源发起跨域请求
  6. res.setHeader('Access-Control-Allow-Origin','*')
  7. next()
  8. }
  9. // 配置路由并使用中间件
  10. app.get('/',corsMiddleWare,(req,res)=>{
  11. res.send('hello!')
  12. })

4.3 단순 요청과 복잡한 요청

CORS는 요청을 ① 단순 요청과 ② 복잡한 요청의 두 가지 범주로 나눕니다.

간단한 요청

복잡한 요청

✅요청 방식(method)은 GET, HEAD, POST입니다.

1은 간단한 요청이거나 복잡한 요청입니다.
2 복잡한 요청은 자동으로 실행 전 요청을 보냅니다.

✅요청 헤더 필드는 다음을 준수해야 합니다."CORS 보안 사양"
간단한 참고 사항: 요청 헤더가 수동으로 수정되지 않는 한 일반적으로 이 사양을 준수할 수 있습니다.

✅요청 헤더의 Content-Type 값은 다음 세 가지만 가능합니다:
●텍스트/일반
●멀티파트/폼데이터
●application/x-www-form-urlencoded

비행 전 요청 관련:

  • 1전송 타이밍: 프리플라이트 요청은 실제 도메인 간 요청 전에 전송되며 브라우저에 의해 자동으로 시작됩니다.
  • 2 주요 기능: 다음 크로스 도메인 요청을 허용할지 여부를 서버에 확인하는 데 사용됩니다.
  • 3기본 프로세스: 먼저 OPTIONS 요청을 시작합니다. 사전 확인을 통과하면 실제 교차 도메인 요청을 계속 시작합니다.
  • 4 요청 헤더 내용: OPTIONS 실행 전 요청에는 일반적으로 다음 요청 헤더가 포함됩니다.

요청 헤더

의미

기원

요청 소스

접근 제어 요청 방법

실제 요청된 HTTP 메소드

액세스 제어 요청 헤더

실제 요청에 사용되는 사용자 정의 헤더(있는 경우)

4.4CORS는 복잡한 도메인 간 요청을 해결합니다.

1 1단계: 서버는 먼저 브라우저의 실행 전 요청을 전달하고 다음 응답 헤더를 반환해야 합니다.

응답 헤더

의미

접근 제어 허용 출처

허용된 소스

액세스 제어 허용 방법

허용된 방법

액세스 제어 허용 헤더

허용되는 맞춤 헤더

접근 제어 최대 연령

실행 전 요청에 대한 결과 캐싱 시간(선택 사항)

이미지.png

2 2단계: 실제 교차 도메인 요청 처리(간단한 교차 도메인 요청을 처리하는 것과 동일한 방식)

이미지.png

서버 코어 코드:

  1. // 处理预检请求
  2. app.options('/students', (req, res) => {
  3. // 设置允许的跨域请求源
  4. res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500')
  5. // 设置允许的请求方法
  6. res.setHeader('Access-Control-Allow-Methods', 'GET')
  7. // 设置允许的请求头
  8. res.setHeader('Access-Control-Allow-Headers', 'school')
  9. // 设置预检请求的缓存时间(可选)
  10. res.setHeader('Access-Control-Max-Age', 7200)
  11. // 发送响应
  12. res.send()
  13. })
  14. // 处理实际请求
  15. app.get('/students', (req, res) => {
  16. // 设置允许的跨域请求源
  17. res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500')
  18. // 随便设置一个自定义响应头
  19. res.setHeader('abc',123)
  20. // 设置允许暴露给客户端的响应头
  21. res.setHeader('Access-Control-Expose-Headers', 'abc')
  22. // 打印请求日志
  23. console.log('有人请求/students了')
  24. // 发送响应数据
  25. res.send(students)
  26. })

4.5 cors 라이브러리를 사용하여 구성을 빠르게 완료

위 구성에서는 응답 헤더를 직접 구성하거나 미들웨어를 수동으로 캡슐화해야 하며, cors 라이브러리를 사용하면 보다 편리하게 구성을 완료할 수 있습니다.

●코르 설치

npm i cors

●간단한 구성 cors

app.use(cors())

●COR를 완전히 구성합니다.

  1. // cors中间件配置
  2. const corsOptions = {
  3. origin: 'http://127.0.0.1:5500', // 允许的源
  4. methods: ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS'], // 允许的方法
  5. allowedHeaders: ['school'], // 允许的自定义头
  6. exposedHeaders: ['abc'], // 要暴露的响应头
  7. optionsSuccessStatus: 200 // 预检请求成功的状态码
  8. };
  9. app.use(cors(corsOptions)); // 使用cors中间件

기본적으로 js는 백엔드에서 설정한 응답 헤더에 액세스할 수 없으며 백엔드에서 노출해야 합니다.

5JSONP는 도메인 간 문제를 해결합니다.

1JSONP 개요: JSONP는 다음을 사용합니다.<script>标签可以跨域加载脚本,且不受严格限制的特性,可以说是程序员智慧的结晶,早期一些浏览器不支持 CORS 的时,可以靠 JSONP 解决跨域。


2기본 프로세스:

  • ○1단계: 클라이언트가<script>标签,并将其src属性设置为包含跨域请求的 URL,同时准备一个回调函数,这个回调函数用于处理返回的数据。
  • ○2단계: 서버는 요청을 받은 후 콜백 함수에 데이터를 캡슐화하여 반환합니다.
  • ○3단계: 클라이언트의 콜백 함수가 호출되고, 데이터가 매개변수 형태로 콜백 함수에 전달됩니다.

3그림:

이미지.png

4가지 코드 예시:

  1. <button onclick="getTeachers()">获取数据</button>
  2. <script type="text/javascript" >
  3. function callback(data){
  4. console.log(data)
  5. }
  6. function getTeachers(url){
  7. // 创建script元素
  8. const script = document.createElement('script')
  9. // 指定script的src属性
  10. script.src= 'http://127.0.0.1:8081/teachers'
  11. // 将script元素添加到body中触发脚本加载
  12. document.body.appendChild(script)
  13. // script标签加载完毕后移除该标签
  14. script.onload = ()=>{
  15. script.remove()
  16. }
  17. }
  18. </script>

5jQuery 캡슐화된 jsonp

?callback=?'은 고정 형식이며 자동으로 구문 분석됩니다.

  1. $.getJSON('http://127.0.0.1:8081/teachers?callback=?',(data)=>{
  2. console.log(data)
  3. })

6교차 도메인 문제를 해결하기 위해 프록시 구성

6.1 프록시 서버 직접 구성

서버와 페이지가 동일한 출처에 있는지 확인하려면 Express를 사용하여 정적 리소스를 시작해야 합니다.

  1. // 启动静态资源 让服务器跟页面同一个源
  2. app.use(express.static("./public"));

http-proxy-middleware로 프록시 구성

  1. const { createProxyMiddleware } = require('http-proxy-middleware');
  2. app.use('/api',createProxyMiddleware({
  3. target:'https://www.toutiao.com',
  4. changeOrigin:true,
  5. pathRewrite:{
  6. '^/api':''
  7. }

 

이점:

  • 풍부한 기능: http-proxy-middleware는 다양한 프록시 요구 사항을 충족하는 풍부한 구성 옵션을 제공합니다.
  • 다중 프록시의 유연한 구성: 다양한 인터페이스 경로에 따라 다중 프록시 서버를 구성할 수 있습니다.
  • 요청 차단 가능: 사용자 정의 처리 기능을 통해 요청을 차단하고 수정할 수 있습니다.

결점:

  • 구성은 비교적 복잡합니다. http-proxy-middleware 라이브러리의 구성 규칙과 매개변수를 이해해야 합니다.
  • 프로덕션 환경에 적합하지 않음: http-proxy-middleware는 주로 개발 환경에서 사용되며 프로덕션 환경에는 적합하지 않습니다.

사용되는 장면:

  • 모든 빌드 도구를 사용하는 프런트엔드 프로젝트에 적합하며 모든 개발 서버에서 작동합니다.
  • 여러 프록시 서버를 유연하게 구성해야 하는 시나리오에 적합합니다.
  • 요청을 가로채서 수정해야 하는 시나리오에 적합합니다.

6.2 Nginx를 사용하여 프록시 서버 구축

전반적인 아이디어는 nginx가 정적 콘텐츠 서버와 프록시 서버라는 두 가지 역할을 수행하도록 하는 것입니다.

nginx 구성을 다음과 같이 수정하세요. nginx의 루트 디렉터리는 C 드라이브가 아닌 것이 좋습니다.

  1. # 配置nginx根目录
  2. location / {
  3. root D:dist;
  4. index index.html index.htm;
  5. }
  6. # 配置代理
  7. location /dev/ {
  8. # 设置代理目标
  9. proxy_pass http://sph-h5-api.atguigu.cn/;
  10. }

2 모든 요청이 /dev로 전달되도록 프런트엔드 프로젝트를 수정한 다음 다시 패키징합니다.

  1. const request = axios.create({
  2. baseURL:'/dev',
  3. timeout:10000
  4. })

그런 다음 nginx 서버에 직접 액세스합니다. 예를 들어 nginx가 포트 8099에서 실행 중인 경우 액세스합니다.

http://localhost:8099

그런 다음 새로 고침 404 문제가 발생하면 nginx 구성을 추가하여 문제를 해결하세요.

  1. # 配置nginx根目录
  2. location / {
  3. root D:dist;
  4. index index.html index.htm;
  5. try_files $uri $uri/ /index.html; # 解决刷新404
  6. }
  7. # 配置代理
  8. location /dev/ {
  9. # 设置代理目标
  10. proxy_pass http://sph-h5-api.atguigu.cn/;
  11. }

이 두 개의 "/"를 추가하면 dev가 제거됩니다.

6.3 스캐폴딩을 활용한 서버 구축

1. vue.config.js 파일을 사용하여 프록시를 구성합니다.

Vue 프로젝트의 루트 디렉터리에 vue.config.js 파일을 만들고 다음 코드를 추가합니다.

  1. module.exports = {
  2. devServer: {
  3. proxy: {
  4. '/api': {
  5. target: 'http://api.example.com',
  6. changeOrigin: true,
  7. pathRewrite: {
  8. '^/api': ''
  9. }
  10. }
  11. }
  12. }
  13. }

위의 코드에서 우리는devServer 프록시 서버를 구성하는 구성 항목입니다.~에proxy속성은 프록시 규칙을 구성하는 데 사용됩니다./api프록시가 필요한 인터페이스 경로를 나타냅니다.target속성은 프록시의 대상 서버 주소를 나타냅니다.changeOrigin속성은 요청의 소스 주소를 변경할지 여부를 나타냅니다.pathRewrite요청된 경로를 재정의하는 데 사용되는 속성입니다.

이점:

  • 간단한 구성: webpack-dev-server의 프록시 구성을 사용하면 webpack 구성 파일에서 간단한 구성만 수행하면 됩니다.
  • 포괄적인 기능: webpack-dev-server는 대부분의 프록시 요구 사항을 충족하는 풍부한 구성 옵션을 제공합니다.
  • 요청 차단 가능: 사용자 정의 처리 기능을 통해 요청을 차단하고 수정할 수 있습니다.

결점:

  • 서버를 다시 시작해야 합니다. 구성이 수정된 후 webpack-dev-server를 다시 시작해야 적용됩니다.
  • 프로덕션 환경에 적합하지 않음: webpack-dev-server는 주로 개발 환경에 사용되며 프로덕션 환경에는 적합하지 않습니다.

사용되는 장면:

  • webpack을 사용하여 구축하고 webpack-dev-server를 통해 개발 서버를 시작하는 프런트엔드 프로젝트에 적합합니다.
  • 간단한 프록시 구성이 필요하고 프록시 구성을 자주 수정할 필요가 없는 시나리오에 적합합니다.