iframe 免登录的核心目标是:在不暴露用户敏感信息的前提下,让页面 A 中嵌套的页面 B 能够自动完成身份验证并展示内容。
iframe 免登录可以分为四个请求
请求1 - 获取 A 页面的 html 文件
这个就是正常请求页面的 html 文件,也是所有页面展示的第一步,这个请求的 path 是这个页面对应的配置路由,如 domain-a.com/pageA
请求2 - iframe 对应的 url
向页面 A 的后端请求获取页面 B 的登录地址 loginUrl
对 iframe 进行封装,封装成组件 <MyIframe path='/page/demo'/> 并在 MyIframe 组件中对于 path 进行请求(Fetch/XHR);这个请求就是 A 后端的一个普通 api 请求,在这个 api 请求中获取域名 A 中用户信息,包括:userId、userType、nickname 之类,然后通过微服务获取嵌入页面的地址;
这里的微服务应该由页面 B 的后端提供,输入参数包含操作者的用户信息、想要获取的页面路径(如 /page/demo)、以及操作者的来源(比如 channelId 之类),最终的返回值是登录地址 loginUrl;
微服务返回结果后,此时仍然处于域名 A 对应的后端服务中。并不需要将 loginUrl 返回,而是直接重定向 this.ctx.redirect(loginUrl)进入请求3,返回状态码为 302
请求3 - 访问 loginUrl
获取上述 loginUrl 后访问该地址,loginUrl 的形式应该类似于 domain-b.com/external?au…
其中 authToken 是微服务中通过计算得到的结果,用于页面 B 进行登录验证;
在访问上述 loginUrl 之后,B 会验证登录信息是否正确,如果正确则 redirect 到不包含验证参数的 URL 上,以上述例子为例对应的 URL 是 domain-b.com/external ;
这个 URL 有两个响应标头需要注意
- Location,这个是之后真实的访问路由,如
/external; - Set-Cookie,这个是验证成功之后的登录验权 cookie,浏览器自动保存, 之后所有 B 中的请求浏览器都会带上这个 cookie
Set-Cookie: JSESSIONID=xyz123; Path=/; Domain=.bbb.com; Secure; HttpOnly
请求4 - iframe 真实的 URL、获取 B 页面的 html 文件
就是请求3里面的重定向的值,这个 URL 会是 MyIframe 组件里最后真实渲染的 URL
useEffect(() => {
if (!isAccessible(path)) return;
request<{ success: boolean; loginUrl: string }>(path).then(res => {
setIframeUrl(res.loginUrl);
});
}, [path]);
其余问题
四个请求分别是哪些域发出的,什么类型请求,返回的状态码是什么
- 请求 12 是 A 发出,请求 34 是 B 发出;
- 请求 23 是 302、请求 14 是200;
- 请求 14 都是 Document,请求 23 可以是 Document、也可以是 Fetch/XHR
推荐将请求 23 类型设置为 Document,这样可以 1. 将所有 Fetch/XHR 请求控制在请求头是 /api 开头的; 2. 整个页面这样就这4个请求是 Document,方便纠错;3. 不需要解决跨域问题
请求 3 重定向是必须的吗,请求 3 存在的意思是什么?
请求 3 必须或者说请求 2 进行重定向到请求 3 是必须的,但是请求 3 结束后重定向并不是必须,可以将请求 3 的 URL 直接作为 iframe 里真实渲染的 URL(这样就不存在请求 4),请求 3 的意义是身份认证 + 登录态建立,是所有步骤中最重要的一步
请求 3 重定向的意义是什么?
虽然可以不进行重定向,但是推荐做,做重定向是一种常见的安全设计模式,意义在于:
- 避免将
authToken、userId等敏感参数暴露在浏览器地址栏; - 保证用户刷新页面时不会携带敏感 token;
- 提升用户体验和安全性;
重定向谁控制,怎么控制?
有两次重定向,第一次是请求2,这个重定向由 A 的后端控制,获取微服务的 loginUrl 后重定向,第二次是请求 3,这个重定向由 B 的后端控制,指向真正的访问地址,隐藏敏感信息
后端代码this.ctx.redirect(URL),这样返回的状态码为 302,相应标头中 Location 为重定向地址
免登过程中有跨域问题吗?
- 请求1和2都是正常请求,不存在跨域问题;
- 请求3和4都是重定向之后的导航请求,不属于Fetch/AJAX,没有跨域问题;
- 但是请求3和4有 Set-Cookie 操作,需要解决 cookie 的跨域问题,一般可以使用 SameSite = None + Secure + Partitioned 解决 iframe 嵌套的跨域问题
如果我是页面 B 的提供方,我想控制页面 B 的嵌入页面,怎么做?
- 首先可以通过用户信息控制,请求 2 中通过微服务获取 authToken,并在请求 3 中校验,用户信息错误报错
- 其次可以通过请求嵌入页面的域来控制,对于不想展示的页面,不对请求 3 响应标头的 Set-Cookie 做跨域处理,这样 cookie 无法存入登录校验失败