GDPR,Cookie同意横幅飘扬网络 [英] GDPR, Cookies consent banner Flutter web

查看:16
本文介绍了GDPR,Cookie同意横幅飘扬网络的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用Ffltter建立我的网站,但Web编程对我来说是非常新的,我不太确定我到底了解Cookie是如何工作的。 我仍然需要了解哪些Cookie要写在哪里,以及我从哪里获取这些Cookie。 构建横幅来管理应该很容易,如果我没有错的话,它应该是首页上弹出的第一件事。 例如,Medium Banner只是一个可忽略的横幅,可以摆动消息 To make Medium work, we log user data. By using Medium, you agree to our Privacy Policy. 带有链接的隐私政策,但它没有任何选择加入,因此看起来不符合GDPR。

此处https://medium.com/@mayur_solanki/flutter-web-formerly-hummingbird-ba52f5135fb0显示了如何在颤动网络中写入和读取Cookie

html.window.localStorage['key']
html.window.sessionStorage['key']
html.window.document.cookie

html.window.localStorage['local_value'] = localStorage;
html.window.sessionStorage['session_value'] = sessionStorage;
html.window.document.cookie= "username=${cookies}; expires=Thu, 18 Dec 2020 12:00:00 UTC";

据我所知,Cookie属于这些类型。

第一方: 跟踪用户行为(页面访问、用户数等)当我使用谷歌分析时,我确实需要征得他们的同意。 这里Google Analytics, Flutter, cookies and the General Data Protection Regulation (GDPR)显示了如何激活/停用它,因此如果我没有错的话,我不应该自己存储任何东西。

第三方: 例如,这些视频来自我主页上的YouTube链接视频,所以我也需要征得同意。 我还没有检查过,但我猜它应该类似于Google Analytics

会话Cookie: 这些应该用来记住购物篮中的物品。 我不应该需要这些的..

持久化Cookie: 这些应该是为了保持用户的登录。 其中一个网页是Retailer access,这是零售商的应用程序(市场的供应方)。 我正在使用Google Sign来登录用户,所以我应该需要这些,因为它在导航到Retailer access时始终向用户显示登录表单,即使他们登录后也是如此。

安全Cookie: 这些仅用于HTTPS,并用于结账/付款页面。 在我的网络中,零售商的应用程序只创建产品,管理车间预订,并处理与客户的沟通。 手机应用程序(市场的需求方)是所有的支付都是使用Strip进行的,所以我应该不需要在Web上存储任何东西,对吗?

抱歉,问题太长了,我希望它足够清楚。 感谢您的帮助。

推荐答案

我基本上遇到了这个问题,因为我也在使用第三方脚本(Firebase、条带等...)在运行这些脚本之前,我需要得到用户的同意。

我围绕Yett(https://github.com/elbywan/yett)构建我的解决方案,它阻止属于先前定义的黑名单的脚本。您甚至可以自己实现此功能,作者编写了一个有趣的medium article

在我的例子中,我只有必要的脚本,所以我构建了一个解决方案,只有在用户同意所有必要的脚本的情况下才能加载Ffltter应用程序。但是,如果需要对用户的Cookie设置进行更细粒度的控制,并且我为&q;Analytics&q;添加了第二个条目作为可能的起点,则调整此解决方案应该不会太难。

我将用户的设置存储在本地存储中,并直接在应用程序Start上检索它们,以创建黑名单并决定是否应该显示Cookie横幅。

这是我的index.html

它引用以下脚本:get_consent.jsset_consent.jsinit_firebase.jsload_app.js(更多信息见下文)。

<!DOCTYPE html>
<html>
<head>
  <!--
    If you are serving your web app in a path other than the root, change the
    href value below to reflect the base path you are serving from.

    The path provided below has to start and end with a slash "/" in order for
    it to work correctly.

    For more details:
    * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
  -->
  <base href="/">

  <meta charset="UTF-8">
  <meta content="IE=Edge" http-equiv="X-UA-Compatible">
  <meta name="description" content="A new Flutter project.">

  <!-- iOS meta tags & icons -->
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="apple-mobile-web-app-title" content="flutter_utils">
  <link rel="apple-touch-icon" href="icons/Icon-192.png">
  <!-- Assigns blacklist of urls based on localStorage (must be placed before yett script) -->
  <script src="get_consent.js"></script>
  <!-- Yett is used to block all third-party scripts that are part of the blacklist (must be placed before all other (third-party) scripts) -->
  <script src="https://unpkg.com/yett"></script>
  <script src="https://js.stripe.com/v3/"></script>

  <title>flutter_utils</title>
  <link rel="manifest" href="manifest.json">
  <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
  <!-- The standard consent popup (hidden by default) -->
  <div id="consent-popup" class="hidden consent-div">
    <h2>We use cookies and other technologies</h2>
    <p>This website uses cookies and similar functions to process end device information and personal data. The processing serves the integration of content, external services and elements of third parties, statistical analysis/measurement, personalized advertising and the integration of social media. Depending on the function, data may be passed on to third parties within the EU in the process. Your consent is always voluntary, not required for the use of our website and can be rejected or revoked at any time via the icon at the bottom right.
    </p>
    <div>
      <button id="accept-btn" class="btn inline">Accept</button>
      <button id="reject-btn" class="btn inline">Reject</button>
      <button id="info-btn" class="btn inline">More info</button>    
    </div>
  </div>
  <!-- Detailed consent popup allows the user to control scripts by their category -->
  <div id="consent-popup-details" class="hidden consent-div">
    <h2>Choose what to accept</h2>
    <div>
      <div class="row-div">
        <h3>Essential</h3>
        <label class="switch">
          <!-- Essentials must always be checked -->
          <input id="essential-cb" type="checkbox" checked disabled=true>
          <span class="slider round"></span>
        </label>    
      </div>
      <p>
      Here you can find all technically necessary scripts, cookies and other elements that are necessary for the operation of the website.
      </p>
    </div>
        <div>
      <div class="row-div">
        <h3>Analytics</h3>
        <label class="switch">
          <input id ="analytics-cb" type="checkbox">
          <span class="slider round"></span>
        </label>    
      </div>
      <p>
      For the site, visitors, web page views and diveerse other data are stored anonymously.
      </p>
    </div>
    <div>
      <button id="save-btn" class="btn inline">Save</button>
      <button id="cancel-btn" class="btn inline">Cancel</button>   
    </div>
  </div>
  <!-- Updates localStorage with user's cookie settings -->
  <script src="set_consent.js"></script>
  <script src="https://www.gstatic.com/firebasejs/8.6.1/firebase-app.js"></script>
  <script src="https://www.gstatic.com/firebasejs/8.6.1/firebase-auth.js"></script>
  <script src="https://www.gstatic.com/firebasejs/8.6.1/firebase-firestore.js"></script>
  <script src="https://www.gstatic.com/firebasejs/8.6.1/firebase-storage.js"></script>
  <!-- Initializes firebase (if user gave consent) -->
  <script src="init_firebase.js"></script>
  <!-- Loads flutter app (if user gave consent) -->
  <script src="load_app.js"></script>     
</body>
</html>

get_consent.js是第一个脚本,它从本地存储检索用户的设置并定义Yett黑名单:

const essentialCookies = ["js.stripe.com", "www.gstatic.com"];
const analyticsCookies = ["www.google-analytics.com"];
const allCookies = [...essentialCookies, ...analyticsCookies];
const consentPropertyName = "cookie_consent";

const retrieveConsentSettings = () => {
  const consentJsonString = localStorage.getItem(consentPropertyName);
  return JSON.parse(consentJsonString);
};

const checkConsentIsMissing = () => {
  const consentObj = retrieveConsentSettings();
  if (!consentObj || consentObj.length == 0) {
    return true;
  }
  return false;
};

const consentIsMissing = checkConsentIsMissing();

var blacklist;
if (consentIsMissing) {
  blacklist = allCookies;
} else {
  const acceptedCookies = retrieveConsentSettings();
  // Remove all script urls from blacklist that the user accepts (if all are accepted the blacklist will be empty)
  blacklist = allCookies.filter( ( el ) => !acceptedCookies.includes( el ) );
}

// Yett blacklist expects list of RegExp objects
var blacklistRegEx = [];
for (let index = 0; index < blacklist.length; index++) {
  const regExp = new RegExp(blacklist[index]);
  blacklistRegEx.push(regExp);
}

YETT_BLACKLIST = blacklistRegEx;

set_consent.js负责使用用户的设置更新本地存储,还隐藏/显示Cookie同意的相应div。通常,只需调用window.yett.unblock()来取消阻止脚本,但由于它们的顺序很重要,我决定在本地存储更新后简单地重新加载窗口:

const saveToStorage = (acceptedCookies) => {
  const jsonString = JSON.stringify(acceptedCookies);
  localStorage.setItem(consentPropertyName, jsonString);
};

window.onload = () => {
  const consentPopup = document.getElementById("consent-popup");
  const consentPopupDetails = document.getElementById("consent-popup-details");
  const acceptBtn = document.getElementById("accept-btn");
  const moreInfoBtn = document.getElementById("info-btn");
  const saveBtn = document.getElementById("save-btn");
  const cancelBtn = document.getElementById("cancel-btn");
  const rejectBtn = document.getElementById("reject-btn");

  const acceptFn = (event) => {
    const cookiesTmp = [...essentialCookies, ...analyticsCookies];
    saveToStorage(cookiesTmp);
    // Reload window after localStorage was updated.
    // The blacklist will then only contain items the user has not yet consented to.
    window.location.reload();
  };

  const cancelFn = (event) => {
    consentPopup.classList.remove("hidden");
    consentPopupDetails.classList.add("hidden");
  };

  const rejectFn = (event) => {
    console.log("Rejected!");
    // Possible To-Do: Show placeholder content if even essential scripts are rejected.
  };

  const showDetailsFn = () => {
    consentPopup.classList.add("hidden");
    consentPopupDetails.classList.remove("hidden");
  };

  const saveFn = (event) => {
    const analyticsChecked = document.getElementById("analytics-cb").checked;
    var cookiesTmp = [...essentialCookies];
    if (analyticsChecked) {
      cookiesTmp.push(...analyticsCookies);
    }
    saveToStorage(cookiesTmp);
    // Reload window after localStorage was updated.
    // The blacklist will then only contain items the user has not yet consented to.
    window.location.reload();
  };

  acceptBtn.addEventListener("click", acceptFn);
  moreInfoBtn.addEventListener("click", showDetailsFn);
  saveBtn.addEventListener("click", saveFn);
  cancelBtn.addEventListener("click", cancelFn);
  rejectBtn.addEventListener("click", rejectFn);

  if (consentIsMissing) {
    consentPopup.classList.remove("hidden");
  }
};

init_firebase.js是初始化服务的常用脚本,但我只有在获得同意的情况下才进行初始化:

var firebaseConfig = {
  // your standard config
};

// Initialize Firebase only if user consented
if (!consentIsMissing) {
  firebase.initializeApp(firebaseConfig);
}

相同的逻辑应用于脚本load_app.js。只有在用户同意的情况下,才会加载Ffltter应用程序。

因此,用户可能会向index.html添加一些备用内容,如果用户拒绝必要的脚本,则会显示这些内容。根据您的使用案例,也可能选择加载应用程序,然后通过从本地存储访问用户设置来在应用程序中进行区分。

var serviceWorkerVersion = null;
var scriptLoaded = false;
function loadMainDartJs() {
  if (scriptLoaded) {
    return;
  }
  scriptLoaded = true;
  var scriptTag = document.createElement("script");
  scriptTag.src = "main.dart.js";
  scriptTag.type = "application/javascript";
  document.body.append(scriptTag);
}

// Load app only if user consented
if (!consentIsMissing) {
  if ("serviceWorker" in navigator) {
    // Service workers are supported. Use them.
    window.addEventListener("load", function () {
      // Wait for registration to finish before dropping the <script> tag.
      // Otherwise, the browser will load the script multiple times,
      // potentially different versions.
      var serviceWorkerUrl =
        "flutter_service_worker.js?v=" + serviceWorkerVersion;
      navigator.serviceWorker.register(serviceWorkerUrl).then((reg) => {
        function waitForActivation(serviceWorker) {
          serviceWorker.addEventListener("statechange", () => {
            if (serviceWorker.state == "activated") {
              console.log("Installed new service worker.");
              loadMainDartJs();
            }
          });
        }
        if (!reg.active && (reg.installing || reg.waiting)) {
          // No active web worker and we have installed or are installing
          // one for the first time. Simply wait for it to activate.
          waitForActivation(reg.installing ?? reg.waiting);
        } else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
          // When the app updates the serviceWorkerVersion changes, so we
          // need to ask the service worker to update.
          console.log("New service worker available.");
          reg.update();
          waitForActivation(reg.installing);
        } else {
          // Existing service worker is still good.
          console.log("Loading app from service worker.");
          loadMainDartJs();
        }
      });

      // If service worker doesn't succeed in a reasonable amount of time,
      // fallback to plaint <script> tag.
      setTimeout(() => {
        if (!scriptLoaded) {
          console.warn(
            "Failed to load app from service worker. Falling back to plain <script> tag."
          );
          loadMainDartJs();
        }
      }, 4000);
    });
  } else {
    // Service workers not supported. Just drop the <script> tag.
    loadMainDartJs();
  }
}

这是我的style.css

html,
body {
  height: 100%;
  width: 100%;
  background-color: #2d2d2d;
  font-family: Arial, Helvetica, sans-serif;
}

.hidden {
  display: none;
  visibility: hidden;
}

.consent-div {
  position: fixed;
  bottom: 40px;
  left: 10%;
  right: 10%;
  width: 80%;
  padding: 14px 14px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  background-color: #eee;
  border-radius: 5px;
  box-shadow: 0 0 5px 5px rgba(0, 0, 0, 0.2);
}

.row-div {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

#accept-btn,
#save-btn {
  background-color: #103900;
}

#reject-btn,
#cancel-btn {
  background-color: #ff0000;
}

.btn {
  height: 25px;
  width: 140px;
  background-color: #777;
  border: none;
  color: white;
  border-radius: 5px;
  cursor: pointer;
}

.inline {
  display: inline-block;
  margin-right: 5px;
}

h2 {
  margin-block-start: 0.5em;
  margin-block-end: 0em;
}

h3 {
  margin-block-start: 0.5em;
  margin-block-end: 0em;
}

/* The switch - the box around the slider */
.switch {
  position: relative;
  display: inline-block;
  width: 50px;
  height: 25px;
}

/* Hide default HTML checkbox */
.switch input {
  opacity: 0;
  width: 0;
  height: 0;
}

/* The slider */
.slider {
  position: absolute;
  cursor: pointer;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: #ccc;
}

.slider:before {
  position: absolute;
  content: "";
  height: 18px;
  width: 18px;
  left: 4px;
  bottom: 4px;
  background-color: white;
}

input:checked + .slider {
  background-color: #2196f3;
}

input:focus + .slider {
  box-shadow: 0 0 1px #2196f3;
}

input:checked + .slider:before {
  -webkit-transform: translateX(24px);
  -ms-transform: translateX(24px);
  transform: translateX(24px);
}

/* Rounded sliders */
.slider.round {
  border-radius: 34px;
}

.slider.round:before {
  border-radius: 50%;
}

这篇关于GDPR,Cookie同意横幅飘扬网络的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆