uni-app实现单点登录需前端只传凭证、跳转并存token,配合后端认证中心完成跳转登录页→获取code/token→换取用户态并持久化,区分H5、App、小程序不同跳转方式,统一用onShow捕获参数,token须存storage且含过期时间与设备标识。
uni-app
怎么实现单点登录?核心就一条:前端不验身份,只传凭证、跳转、存token
uni-app本身没有内置SSO能力,它只是个渲染层。所谓“实现单点登录”,本质是配合后端认证中心(如OAuth2服务、浙里办SSO网关、自建JWT中心)完成三件事:跳转到统一登录页 → 拿回code/token → 换取用户态并持久化。不能指望
或
帮你搞定SSO——那是微信小程序专属逻辑,跨平台时完全失效。
常见错误现象包括:H5能跳但白屏、App端跳转后无响应、小程序里
报错未定义、跳转URL拼错参数导致401。根本原因是混淆了“平台原生登录”和“业务级单点登录”——前者是微信/支付宝提供的账号体系,后者是你自己系统定义的登录态流转规则。
必须区分环境:H5走
,App用
或
,小程序只能用
或跳外链(需配置业务域名)
redirectUrl参数要URL编码,否则含
或
会截断;例如
不要在登录页
里直接发请求拿用户信息——此时token可能还没写入storage,建议跳转后在目标页
中校验
怎么处理从SSO服务跳回来的code或token?别硬解析URL,用uni-app生命周期接管
很多开发者习惯在首页
里手动取
或
,但这是高危操作:H5没有
,App可能被深度链接唤起但参数不在
里,小程序还分
和
两套参数体系。
正确做法是统一用
+
+
组合捕获入口参数:
拿到冷启动参数(首次打开App/H5)
监听热启动,但注意它在Vue实例创建前执行,不能访问
最稳妥——只要页面显示就会触发,且能访问完整Vue上下文,适合做token刷新、用户态同步
示例逻辑:
token存哪?localStorage、storage、Vuex全都不够用
是底线要求,但仅靠它远远不够。SSO场景下,你很可能需要同时维护:登录态(token)、用户基础信息(user)、设备标识(device_no)、过期时间(expires_in)。一旦用错存储位置,就会出现“App登出后H5仍显示已登录”“同一账号在两台手机上互踢失败”等问题。
uni-app电商系统实战精讲课程课件
uni-app电商系统实战精讲课程
下载
关键差异点:
是跨页面、跨进程有效的,但**不跨端**:App端存的token,H5打不开;反之亦然
Vuex或Pinia只在当前Vue实例内有效,页面刷新即丢失,不能替代持久化
如果支持“多端同步登出”,必须让后端提供
接口,并在登出时主动调用,不能只清本地storage
建议结构:
为什么H5部署后SSO跳转总白屏?90%卡在publicPath和router.base配置上
不是代码问题,是构建配置没对齐。uni-app默认输出路径是
,但Nginx或CDN通常期望根目录访问;更致命的是
里
和
写错,会导致JS/CSS加载404,整个页面空白,连console都看不到。
必须核对三项:
中
设为
(相对路径),不能是
或空字符串
也必须是
,尤其当你的H5部署在子路径如
时,base要对应写
Nginx配置中
的
必须指向
目录,且开启
兜底
典型Nginx配置片段:
漏掉
,路由刷新直接404;alias末尾少斜杠,CSS路径拼错;这些都不会报JS错误,只会静默白屏。
SSO最难的从来不是跳转那一下,而是token怎么在不同端之间保持一致、过期怎么感知、登出怎么广播。别迷信“一次登录处处可用”的宣传话术——每个端的运行环境、存储机制、网络策略都不同,得按实际跑通的链路一环一环抠。
uni.login()uni.getUserInfo()locationwindow.location.hrefuni.navigateTo({url: 'https://xxx'})plus.runtime.openURL()uni.navigateToMiniProgram/?encodeURIComponent('/pages/index/index')onLoadonShowonLoadoptions.codeoptions.tokenoptionsoptionsquerysceneuni.getLaunchOptionsSync()uni.onLanuchuni.onShowuni.getLaunchOptionsSync()uni.onLaunchthisuni.onShowonShow() {
const options = uni.getLaunchOptionsSync()
const code = options.query?.code || options.referrerInfo?.extraData?.code
if (code && !uni.getStorageSync('token')) {
this.exchangeCodeForToken(code)
}
}uni.setStorageSync('token', xxx)uni.setStorageSync/logout?device_no=xxx// 存储时
uni.setStorageSync('auth', {
token: 'xxx',
user: { uid: 123, name: '张三' },
expires_at: Date.now() + 7200000, // 2小时
device_no: plus.device.uuid // App端
})
// 校验时
const auth = uni.getStorageSync('auth')
if (!auth || auth.expires_at < Date.now()) {
uni.removeStorageSync('auth')
uni.redirectTo({ url: '/pages/login/login' })
}distmanifest.jsonh5.publicPathh5.router.basemanifest.jsonh5.publicPath"./""/"h5.router.base"./"https://a.com/app/"/app/"location /rootdisttry_files $uri $uri/ /index.html;location / {
alias /var/www/my-uniapp/dist/;
try_files $uri $uri/ /index.html;
}try_files