多页面应用登录状态共享:基于弹出窗口的通用解决方案

今天分享一个常见的场景:多页面应用怎么复用登录状态?比如微前端架构下,多个子应用都需要登录,但每个都搞个登录页太麻烦,用户体验也不连贯。那我们能不能做一个统一的登录页,登录一次,所有页面都同步登录状态呢?当然可以!下面就来分享我们的方案。

注意:这个方案不太适用移动端,但使用Cookie来实现登录状态同步的思路是一致的

思路很简单

我们打算这样干:

  1. 在任何需要登录的页面,点击登录按钮,弹出一个新窗口打开统一的登录页。
  2. 用户在登录页完成登录,后端设置Cookie(确保域名相同,路径为根路径)。
  3. 登录成功后,登录页通过window.opener通知父窗口,然后关闭自己。
  4. 父窗口检测到登录完成,执行回调函数,更新本地登录状态。

这样,同一个域名下的所有页面都能共享Cookie中的登录状态,实现一次登录,处处通行。

代码实现

父页面逻辑

我们封装一个useLogin钩子,主要做两件事:打开登录窗口和监听登录结果。

javascript 复制代码
import { isFunction } from "@/utils/common"

export function useLogin() {
    let loginPage = null
    
    const login = (options = {}, cb = null) => {
        const url = 'pages/login/index'
        // 新窗口打开登录页,设置好窗口参数
        loginPage = window.open(url, '_blank', 'width=375,height=640,status=0,titlebar=0,toolbar=0,resizable=0,menubar=0,location=0,directories=0,left=100,top=100')
        loginPage.focus()
        // 避免有些浏览器焦点没抢到,再试一次
        setTimeout(() => loginPage.focus(), 10)
        
        // 监听焦点事件,判断登录窗口是否关闭
        window.onfocus = async function(activeCall) {
            setTimeout(() => {
                // 如果登录窗口关闭或者登录页主动通知(activeCall为"1")
                if (loginPage?.closed || activeCall == "1") {
                    loginPage = null
                    window.onfocus = null
                    // 执行回调
                    if (cb) {
                        if (isFunction(cb)) {
                            cb(activeCall)
                        } else {
                            console.error('回调函数不是Function类型')
                        }
                    }
                }
            }, 100)
        }
    }
    
    return {
        login
    }
}

登录页逻辑

登录页在登录接口调用成功后,通知父窗口,然后关闭自己。

javascript 复制代码
// 登录接口调用成功后
try {
    // 通知父窗口,传递参数"1"表示登录成功
    await window.opener.onfocus("1")
} catch(e) {
    // 如果通知失败,比如父窗口已经关闭,我们打印错误
    console.log(e)
}
// 关闭登录窗口
window.close()

关键点说明

1. 窗口通信

我们用了window.opener来获取打开当前窗口的父窗口,然后调用父窗口的onfocus方法。这种方法简单直接,但要注意跨域限制,所以我们的登录页和父页面必须是同域名的。

2. 回调函数处理

父窗口通过回调函数来响应登录结果,例如更新对应的用户数据。我们做了类型检查,确保回调是函数才执行。

3. Cookie设置

登录成功后,后端设置Cookie时,要设置path=/domain为主域名,这样所有子页面都能读到。

遇到的问题和解决方案

浏览器拦截弹窗?

有的浏览器可能会拦截window.open,所以我们最好在用户点击事件中触发登录窗口打开,避免异步打开。

移动端怎么办?

移动端不建议用新窗口,可以直接跳转到登录页,登录成功后跳转回来。我们可以根据设备类型选择不同的策略。

总结

这个方案已经在我们项目中稳定运行,用户体验良好。代码量不多,但实现了多页面登录状态共享。当然,还有优化空间,比如加入登录状态过期自动跳转等。

相关推荐
new code Boy24 分钟前
escape谨慎使用
前端·javascript·vue.js
奶球不是球25 分钟前
elementplus组件中el-calendar组件自定义日期单元格内容及样式
javascript·css·css3
叠叠乐42 分钟前
robot_state_publisher 参数
java·前端·算法
Kiri霧42 分钟前
Range循环和切片
前端·后端·学习·golang
小张快跑。1 小时前
【Java企业级开发】(十一)企业级Web应用程序Servlet框架的使用(上)
java·前端·servlet
傻啦嘿哟1 小时前
实战:用Splash搞定JavaScript密集型网页渲染
开发语言·javascript·ecmascript
小白阿龙1 小时前
Flex布局子元素无法垂直居中
前端
秋田君1 小时前
前端工程化部署入门:Windows + Nginx 实现多项目独立托管与跨域解决方案
前端·windows·nginx
冰敷逆向1 小时前
苏宁滑块VMP深入剖析(一):解混淆篇
javascript·爬虫·安全·web
江城开朗的豌豆2 小时前
阿里邮件下载器使用说明
前端