您的位置  > 互联网

同源策略:登录京东后,你需要知道的一些事

1995年,浏览器(前身)首先引入了同源策略。

两个不同的域名不能互相访问,即不能使用JS对象访问。

2. 第三方

如果通过一定的方案,将域名A写在域名B的下面,这就是第三方。

登录京东后,在F12中看到,这次登录操作写入了20多个条目,其中没有一个属于域名。 这是第三方。

这种跨域写入的前提是目标域名愿意并主动允许京东进行写入。 也就是说,这些其他域名都与京东有合作关系。 他们的目的通常是共享用户信息。 用户登录京东后,其他域名可以获取您的用户信息。

3、为什么要以这种方式共享用户信息?

如果其他域名只是想收集您的用户信息,现阶段无需这样做。 他们可以直接在后台将数据复制到未知的地方。

为什么要把这个动作放在浏览器端,让别人按F12就能一目了然呢?

因为他们想要达到这样的效果,根据每个用户的差异来展示一些个性化的页面内容。 典型的应用场景有两种:

当您在百度上搜索某个内容时,您还可以在百度以外的网站上看到相同关键字的广告。 这里的用户信息可能来自第三方。

我以为只有百度和微信知道我考研,原来简书也知道我考研?

所谓无感知登录,是指在您对某个域名进行任何登录操作之前,甚至根本没有访问过某个域名之前,您就已经登录了该域名。 以京东为例,我登录京东后,域名下有一个它写的第三方,就是京东药房的域名。 我以前从未访问过京东大药房,甚至不知道有这样一个域名的存在。 当我第一次在浏览器中输入/时,发现我已经登录了,并且页面上已经显示了我。 京东用户名,

4. 浏览器路线图

在开发第三方的过程中(具体代码怎么写后面会讲),我们发现浏览器可以写第三方,但是切换到浏览器不行。

这个问题比较复杂,与浏览器厂商和版本有关。 从长远来看,计划是在2023年底之前取消所有第三方支持。上面我们看到的从京东到京东药房的写入方法可能会在年底更新浏览器后失效。 2023年,我们可以关注到那时是否还有替代方案。 事实上,它本身就是一项过时的技术。 近年来,已使用Local。 我觉得如果直接把第一方和第三方取消的话,问题也不大。

目前,浏览器在某些条件下允许第三方编写:

为什么浏览器要规定这两个条件呢? https真的更安全吗? 我不这么认为。 所有的跨域限制只会增加程序员的负担。 如果黑客真想攻击,办法还是有的。 唯一受影响的是我们这些合法调用接口的人。 (小区封闭管理并不能防贼,只是让合法居民进出更加麻烦~)

由于该方案需要HTTPS,因此HTTPS证书需要花钱购买。 一般公司的测试环境没有,所以可能无法在测试环境中进行测试。 我这里做的是在生产环境购买https证书,然后写一个demo直接放到生产环境中验证效果。 利用生产环境进行测试,可以最准确、最真实地识别问题,有效避免误报。 这种模式值得各大企业借鉴,应该大力推广。

(顺便说一句,我今天从游玉溪的知乎回答中学到了“假”这个词。)

5.Vue和Boot代码实现方案

1997年,贝尔实验室的专家提议在HTTP协议中添加Set-头。 如果服务器返回给浏览器的响应中带有Set-头,则可以写入当前域名。

我们需要向域名B写入,这个响应是从域名B的服务器发出来的。域名B必须准备一个接口,供其他人请求。 只有提出请求后才会有响应。

假设域名B后端使用Boot,定义一个公共接口(这个方案必须是Get请求),让别人发出请求。

这里有一个特殊的配置,=“/;=UTF-8”。 为什么要这样配置呢?

如果我通过提交表单来请求这个接口,并且每次请求都会打开一个新的浏览器选项卡,那么此时我们请求的是一个很普通的HTTP接口,直接写一个就可以了,不需要指定 / 。

实际应用场景肯定不是Form提交,肯定是100% Ajax请求。 只有Ajax请求才能让用户无意识。 既然是Ajax请求,请注意,我们是从域名A写入域名B。域名A需要请求域名B提供的接口,这是Ajax跨域请求,无法请求。 那么我们现在的问题是如何让这个ajax跨域呢?

解决Ajax跨域的方案有很多。 我查了一下,发现都很麻烦。 如果使用 CORS,则存在预检查请求。 每个请求都是买一送一。 这是一个额外的请求,看起来很烦人。 还有后端写过滤。 当然,安装设备等也比较麻烦。 这里我用来解决跨域问题的JSONP方案可能不是唯一的方案,但一定是最简单的,总共代码不多。 前面之所以指定=“/;=UTF-8”这样的配置,其实就是JSONP的返回类型。 只要是JSONP请求,就会返回该类型。 我们就按照规范来写这句话。 其实这句话不写也可以。 我以前从来没有写过。 很普通,功能也正常。 后来同事说我不符合标准……

使用 JSONP 还意味着我们的请求必须是 Get 请求,而不是 Post 请求,所以你看到我使用 @ 而不是 @ 接收参数。

决定使用JSONP方案后,我们在域名A的前端代码中这样写,

.json 加上 JSONP 依赖项

  "dependencies": {
    "vue-jsonp": "^2.0.0",
  },

在Vue项目入口处引入JSONP依赖(一般是js文件)

import { VueJsonp } from 'vue-jsonp'
Vue.use(VueJsonp)

业务代码,向域名A请求域名B的接口

            let vueInstance = new Vue(); // JSONP库挂载在Vue实例上
            let domain = "www.domainb.com"; // B域名,你的域名是什么就写什么
            let url = "https://" + domain + "/controller/crossSiteCookie"; // 注意这里一定是https开头的,否则Chrome浏览器不会跨域写入Cookie
            vueInstance.$jsonp(url, {
                targetDomain: "123", // 以下这些都是传的参数,和普通的Rest请求参数没区别,根据你的业务需要来设计即可 
                appId: "123",
                userId: "haoyu",
                random: "浩宇"
            })

这种写法看起来和平常的axios或者ajax请求不一样,尤其是回调的语法不一样。 回调作为参数传入。 其实它的执行效果和其他Ajax一模一样,都是在用户察觉不到的情况下发送HTTP请求。 你可以把这一段理解为跨域Ajax请求的固定语法。

这个请求发送到域名B,域名B的Boot写法如下

    /**
     * 跨域写入Cookie
     *
     */
    @RequestMapping(value="/crossSiteCookie", produces = "application/javascript;charset=UTF-8")
    @ResponseBody
    public String crossSiteCookie(@RequestParam("userId") String userId, // 这些都是普通参数,根据具体业务设计即可
                                  @RequestParam("targetDomain") String targetDomain,
                                  @RequestParam(name = "callbackQuery", required = false) String callbackQuery,
                                  @RequestParam("random") String random,
                                  HttpServletRequest httpServletRequest,
                                  HttpServletResponse httpServletResponse) {
        addOneCookie(httpServletResponse, "username", "haoyu", "domainb.com");
        addOneCookie(httpServletResponse, "username", "haoyu", "www.domainb.com");
        return callbackQuery + "('success')";
    }
    /**
     * 往响应头添加一个Cookie,要传三个参数: Cookie键,Cookie值,Cookie所在域名
     */
    public static void addOneCookie(HttpServletResponse httpServletResponse, String cookieKey, String cookieValue, String cookieDomain) {
        // Cookie过期时间
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.HOUR, 6); // 6小时后过期
        Date date = cal.getTime();
        SimpleDateFormat sdf = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.US);
        String responseHeaderString = cookieKey + "=" + cookieValue + ";";
        httpServletResponse.addHeader("Set-Cookie",responseHeaderString + "Path=/; Domain=" + cookieDomain + "; Expires=" + sdf.format(date) + "; SameSite=None; Secure"); // Chrome浏览器跨域写Cookie需要带的属性
    }

这个方法的返回值是没有意义的。 我只是随便写的。 实际的写入操作是在“.”这句话中。

执行的效果是浏览器会写入B域名,整个过程用户不会感知,也不会打开新标签页。 用户已经拥有B下的域名,而根本没有访问过域名B。

另外,我还有一个猜测:即使B域名的页面根本不存在(只有后端界面,没有前端页面),我的计划也会写入B域名。因为浏览器有从始至终都没有访问过域名B的页面,如果该页面不存在,则整个过程即可完成。