1. 问题描述
- 在做项目的时候,可能会遇到需要内嵌第三方页面或者第三方软件厂商内嵌我们自己做的web页面(这里说的内嵌方式是用iframe的方式),当在使用最新版本的“谷歌浏览器”时就会出现无法正常登录的问题,内嵌的页面无法正常的显示,只能显示登录页面,而且输入正确的用户名和密码也无法跳转到展示页面;
- 我从网上找了很多的博客,发现大部分写的都是原理和一些比较简单的解决方案,试过后也还是没法解决这个问题,有的还很麻烦,所以自己在解决了该问题后,我把自己遇到的问题及问题原因和解决方案详细的写一下,帮助大家很快的解决掉该问题
- 问题案例:
公司有个需求,需要将做的各个系统的程序页面集成到一个环境中来,最好的实现方案就是使用iframe内嵌的方式,将其他系统的页面集成到页面中来,但是有个问题,其他系统在登录的时候,都是需要用cookie获取token等跟鉴权相关的信息,如果是使用该种方式,那么在用谷歌88或以上版本访问的时候则不能正常的访问,直接跳转到登录页面,且在该登录页面重新输入用户名和密码后再点击登录,接口会请求成功,但是无法跳转到展示页面。但是用其他除了谷歌内核的浏览器是正常的,比如火狐。
2. 问题原因
- 经过排查是因为拿不到token,拿不到token的原因是:由于跨域问题导致,我们框架里把token存储在了cookie中,开始我们系统用的cookie存储的token,由于另一个token的信息是在iframe的src中,这样就导致获取不到token。
- Google 在2020年2月4号发布的 Chrome 80 版本(schedule:https://www.chromestatus/features/schedule)中默认屏蔽所有第三方 Cookie,即默认为所有 Cookie 加上 SameSite=Lax 属性(https://www.chromestatus/feature/5088147346030592),并且拒绝非Secure的Cookie设为 SameSite=None(https://www.chromestatus/feature/5633521622188032), SameSite的作用就是防止跨域传送cookie,从而防止 CSRF 攻击和用户追踪,此举是为了从源头屏蔽 CSRF 漏洞。关于 SameSite 属性的介绍,《Cookie 的 SameSite 属性》。
上述的问题原因是我从网上找到的写的比较详细一些的,原文连接是:chrome浏览器iframe嵌套页面跨域无法获取cookie问题
简单来说就是因为谷歌的最新版本导致的该问题,在使用不同ip端口页面访问的时候,无法设置和获取到cookie
因为在嵌套的页面对应的系统中,需要用到cookie去获取token用于接口查询等操作时的请求头中的参数项,所以如果获取不到的时候,就会出现异常,导致无法正常登录、接口无法正常查询
上图是正常请求的情况,能够正常的响应数据,下图是异常的情况,没有传Authorization、Cookie等重要的信息:
3. 解决方案
3.1. 因为无法获取到cookie,那么当将cookie中保存的一些信息都保存到sessionStorage或localstorage中的话,就不会存在该问题,所以我就将token信息保存到了sessionStorage中,当获取cookie中的token获取不到的时候,那么再去获取sessionStorage中的token,这样如果sessionStorage中保存了的话,该问题就解决掉了,具体的代码如下:
import Cookies from "js-cookie";
import cache from "@/plugins/cache";const TokenKey = location.host + "_DcAdmin-Token";const isDefaultLogin = process.env.VUE_APP_IS_DEFAULT_LOGIN;export function getToken() {let token = "";if (!Cookies.get(TokenKey)) {token = cache.session.get(TokenKey);} else {token = Cookies.get(TokenKey);}return token;
}export function setToken(token) {if (isDefaultLogin == "true") {cache.session.set(TokenKey, token);}return Cookies.set(TokenKey, token);
}export function removeToken() {cache.session.remove(TokenKey);return Cookies.remove(TokenKey);
}
VUE_APP_IS_DEFAULT_LOGIN是设置的一个参数,用于设置是否默认登录,即自己在配置文件中设置默认登录的用户名和密码,实现自动登录,也可以是使用单点登录鉴权。在代码中增加“ if (isDefaultLogin == “true”) ”的判断还有一个原因是:不需要在所有情况下都保存到sessionStorage中,只有在需要内嵌系统页面的时候才需要存到sessionStorage,其他情况下不需要保存,保证了系统的安全性,避免被破解攻击,因为使用cookie的安全性比sessionStorage会更高一些。
- plugins/cache的代码
const sessionCache = {set (key, value) {if (!sessionStorage) {return}if (key != null && value != null) {sessionStorage.setItem(key, value)}},get (key) {if (!sessionStorage) {return null}if (key == null) {return null}return sessionStorage.getItem(key)},setJSON (key, jsonValue) {if (jsonValue != null) {this.set(key, JSON.stringify(jsonValue))}},getJSON (key) {const value = this.get(key)if (value != null) {return JSON.parse(value)}},remove (key) {sessionStorage.removeItem(key);}
}
const localCache = {set (key, value) {if (!localStorage) {return}if (key != null && value != null) {localStorage.setItem(key, value)}},get (key) {if (!localStorage) {return null}if (key == null) {return null}return localStorage.getItem(key)},setJSON (key, jsonValue) {if (jsonValue != null) {this.set(key, JSON.stringify(jsonValue))}},getJSON (key) {const value = this.get(key)if (value != null) {return JSON.parse(value)}},remove (key) {localStorage.removeItem(key);}
}export default {/*** 会话级缓存*/session: sessionCache,/*** 本地缓存*/local: localCache
}
3.2. 在系统中,不光是token需要存储到cookie中,像用户名、密码等简单的一些信息可能也需要保存到cookie中,那么也需要我们将这些信息保存到sessionStorage中,所以需要写一个公共的设置,统一的设置cookie的信息,具体代码如下:
// 设置cookie的值 解决跨域的问题
export function getCookie(key) {let value = "";if (!Cookies.get(key)) {value = cache.session.get(key);} else {value = Cookies.get(key);}return value;
}export function setCookie(key, value) {if (isDefaultLogin == "true") {cache.session.set(key, value);}return Cookies.set(key, value);
}export function removeCookie(key) {cache.session.remove(key);return Cookies.remove(key);
}
下面是设置sessionStorage后的效果:
3.3. 浏览器存储值 Cookie、sessionStorage、localstorage的区别,及都在哪些场景中可以使用
首先,Cookie、sessionStorage和localStorage都是用于在浏览器端存储数据的机制,但它们在用途、生命周期和存储容量等方面有一些区别。
- Cookie:
- 生命周期: 可以设置过期时间,可以是会话级的(浏览器关闭就失效)或持久性的(在指定过期日期前有效)。
- 存储容量: 通常每个Cookie的大小受限制,一般为几KB。
- 用途: 主要用于在客户端存储会话信息,如用户登录状态、购物车内容等。由于每次请求都会携带Cookie,可能会影响性能。
- sessionStorage:
- 生命周期: 存储在 session 中,页面会话结束时(例如关闭标签页或浏览器)数据被清除。
- 存储容量: 比Cookie稍大,通常在5-10MB左右。
- 用途: 适合临时保存一些页面会话期间需要用到的数据,不同页面之间无法共享。
- localStorage:
- 生命周期: 持久性的存储,除非被显式删除或页面被清除,否则数据将一直存在。
- 存储容量: 比sessionStorage更大,通常在5-10MB左右。
- 用途: 适合永久性地存储一些本地的用户设置、历史记录等信息,不同页面之间可以共享。
- 场景中的使用:
Cookie: 适合在服务器和客户端之间传递小段数据,比如用户登录状态的保持,购物车信息等。
sessionStorage: 适合在同一个页面会话期间保持数据,比如表单临时数据,页面之间的传递。
localStorage: 适合永久性存储,如用户偏好设置、本地缓存数据等。
综上所述,选择使用哪种存储方式取决于你的需求,是临时性的、会话级的还是需要长期保存的。
4. 总结
上述描述的内容就是我自己的解决方案,主要解决了谷歌浏览器高版本浏览器88或以上,引用iframe内嵌页面,因为浏览器的安全策略存在跨域,无法使用cookie获取到token信息,导致页面加载不出来的问题,还有其他的解决方案可以在下面给我留言,或者有不懂的地方也可以在下面留言,喜欢的话可以给个关注,会不定时的写一些在开发过程中遇到的一些问题^ - ^