记一次项目迁移遇到的跨域问题

406 阅读5分钟

1.什么是跨域

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

2.常见的跨域场景

3.什么是同源策略? (所谓同源是指:“域名”、“协议”、“端口”均为相同)

同源策略/SOP(Same origin policy)是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。

同源策略限制以下行为

  1. Cookie、LocalStorage 和 IndexDB 无法读取
  2. DOM 和 Js对象无法获得
  3. AJAX 请求不能发送

注意:跨域限制是浏览器的机制,如果直接在服务端请求,是不会触发跨域限制的。

postman就没有跨越问题,是吧

5.跨域解决方案:

1.通过jsonp跨域

2.document.domain + iframe跨域

3.location.hash + iframe

4.window.name + iframe跨域

5.postMessage跨域

6.跨域资源共享(CORS)

7.nginx代理跨域

8.nodejs中间件代理跨域

9.WebSocket协议跨域

10.跨域插件

都是网上说的,可以自行百度

6.什么是JSONP?

JSONP(JSON with Padding)是资料格式 JSON 的一种“使用模式”,可以让网页从别的网域要资料。由于同源策略,一般来说位于 server1.example.com 的网页无法与不是 server1.example.com 的服务器沟通,

而 HTML 的 script 元素是一个例外。利用 script 元素的这个开放策略,网页可以得到从其他来源动态产生的 JSON 资料,而这种使用模式就是所谓的 JSONP。

7. 跨域资源共享(CORS)

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。

它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

普通跨域请求:只服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置。

目前,所有浏览器都支持该功能,CORS也已经成为主流的跨域解决方案。

8. JSONP 和 CORS 比较

JSONP 只能实现 GET 请求,而 CORS 支持所有类型的 HTTP 请求。使用 CORS ,开发者可以是使用普通的 XMLHttpRequest 发起请求和获取数据,比起 JSONP 有更好的错误处理。虽然绝大多数现代的浏览器都已经支持 CORS,但是 CORS 的兼容性比不上 JSONP,一些比较老的浏览器只支持 JSONP

9. 公司内部已经完成了cors安全框架的升级,还有什么问题?

1.业务场景:hpo业务,使用自研的插件,抓取网页信息,提交到内部系统处理

2.问题:访问时请求头Origin的内容是 moz-extension://85368d52-0bdf-46a1-b779-fc0284ad7cb9(随机key) chrome-extension://calbhnhnncknhdpanheaacmnhiebajpn(随机key) 被公司跨域框架配置拦截。

3.为什么不能走配置?

公司框架文档有标注,应该最少配置到一级域名,这种Origin路径格式,公司框架不能识别

10. 那升级框架至今,插件问什么还能用??

因为插件所使用的项目没用公司的那套框架协议

是使用以下过滤器后端完成跨域

public class CorsFilter extends ExternalFilter {
    private static MonitorLogger monitorLogger = MonitorLogger.getInstance();
    @Override
    public boolean external(HttpServletRequest request, HttpServletResponse response) throws BizException {
        /*
         * Access-Control-Allow-Origin 指定该响应的资源是否被允许与给定的origin共享
         */
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("origin"));
        /*
         * 表示是否可以将请求的响应 暴露给页面
         */
        response.setHeader("Access-Control-Allow-Credentials", "true");

        response.addHeader("Access-Control-Max-Age", "3600");

        if ("OPTIONS".equals(request.getMethod())) {
            /*
             * 在对preflight request (预检请求)的应答中明确了客户端要访问的资源允许使用的方法或方法列表
             */
            response.addHeader("Access-Control-Allow-Methods", "POST,GET,TRACE,OPTIONS");

            /*
             * Access-Control-Allow-Headers 用于preflight request(预检请求)中,
             * 列出将会在正式请求Access-Control-Expose-Headers字段中出现的头部信息
             *
             * X-Requested-With  ajax请求
             */
            Enumeration<String>  headers=request.getHeaderNames();
            StringBuilder header=new StringBuilder();
            while (true){
                if(headers.nextElement()==null){
                    header.delete(header.lastIndexOf(","),header.length());
                    break;
                }
                header.append(headers.nextElement());
                header.append(",");
            }

            response.addHeader("Access-Control-Allow-Headers",header.toString());
            return false;
        }
        return true;
    }
    public int priority() {
        return 6;
    }

11. 能用还说这么多,现在哪有问题??

原插件使用项目前端下线,代码迁移到salesassist。借此机会后端也推动项目下线,代码迁移salesassist。

所以插件访问地址会发生变更。

反正要变更插件代码,顺便兼容公司跨域体系。

又回到了序号9的问题

12. 怎么解决??

基于公司现有体系和工作量考虑,采用cors设置请求头的方案

此解决方案完全是前端控制

background.js

const extentionID = chrome.runtime.id;
chrome.webRequest.onBeforeSendHeaders.addListener(
  function (details) {
    // 只处理本插件的请求
    if (details.initiator === `chrome-extension://${extentionID}`) {
      details.requestHeaders = details.requestHeaders.map(({ name, value }) => {
        if (name === "Origin") {
          return { name, value: "http://salesassist.tongdao.cn" };
        }
        return { name, value };
      });
    }

    return { requestHeaders: details.requestHeaders };
  },
  {
    urls: ["http://salesassist.tongdao.cn/*"],
  },
  ["blocking", "requestHeaders", "extraHeaders"]
);

manifest.json

添加

"permissions": [
    "http://*.tongdao.cn/*",
    "contextMenus",
    "notifications",
    "tabs",
    "activeTab",
    "webRequest",
    "webRequestBlocking"
  ],

13. 总结

1.同源策略出于哪些方面的安全考虑?

同源政策的目的是为了防止恶意网站窃取用户数据信息冒充用户做一些操作。同源限制只是提高攻击成本。

如果没有JavaScript同源限制最常见的问题:

(1)CSRF攻击

(2)XSS攻击

2.CORS

浏览器将CORS请求分成两类:简单请求(simple request)非简单请求(not-so-simple request)。

只要同时满足以下两大条件,就属于简单请求。

  1. 请求方法是以下三种方法之一: HEAD GET POST (2)HTTP的头信息不超出以下几种字段: Accept Accept-Language Content-Language Last-Event-ID Content-Type:只限于三个值application/x-www-form-urlencodedmultipart/form-datatext/plain

ae0ff973-7ae6-44a8-b257-b07d4a3f6d05.svg

思考问题

火狐浏览器

background.js

function rewriteUserAgentHeader(e) {
  for (const header of e.requestHeaders) {
    if (header.name.toLowerCase() === "origin") {
      header.value = "http://salesassist.tongdao.cn";
    }
  }
  return { requestHeaders: e.requestHeaders };
}
browser.webRequest.onBeforeSendHeaders.addListener(
  rewriteUserAgentHeader,
  {
    urls: ["http://salesassist.tongdao.cn/*"],
  },
  ["blocking", "requestHeaders"]
);

产生问题有两方面

1.正常同域没有特殊的情况是不会发起OPTIONS请求的,浏览器认为是跨域,触发浏览器自动发起OPTIONS请求。

2.1)跨域请求失败,是因为返回头部没有Access-Control-Allow-Origin

2)没有返回是因为请求Origin和服务器是相同域名,服务器不认为跨域,没组装返回信息Access-Control-Allow-Origin

3)浏览器认为OPTIONS请求没有Access-Control-Allow-Origin,就是跨域失败。所以就抛异常了