扫码登录+反向代理鉴权
背景
wushf.top
下挂载的服务会越来越多,大多数服务是拉取自第三方仓库。
导致每个服务都有自己的鉴权方式,需要维护多套密码。
原作者未必为自己的项目提供单点登录的接口,需要自己考虑解决方案,暂行的方案是:
- 自己开发单点登录,通过SetCookie为合法用户授权。
小程序登录方案
常见的解决方案是提供一个正方形的标准二维码,扫码登陆。
但是这个要多方备案,还要认证成个体工商户或者企业,需要交钱,比较麻烦。
暂行的方案是:
- 扫小程序码,在小程序中与后端通信,实现鉴权登录。
登录流程
网页端请求生成新的小程序二维码
根据官方文档,可以携带一个scene
。
生成一个sessionId
作为会话的唯一标识,将会话作为scene
加入到微信小程序二维码中。
- 手机扫码解析得到
sessionId
,就知道了“我要授权哪个会话”。
二维码有扫描状态,被一个用户扫描之后应不能再被其他用户扫描。
- 手机扫码时应检查是否已被其他用户扫码。
网页轮询当前会话的状态,如已被扫描,那么登录成功,跳转到下一级页面。
这只是大概思路,授权涉及系统的安全性,目前开放的code
服务会允许用户借助code-server
服务修改开发机文件,因此必须要做好鉴权。
不碰撞地生成sessionId
生成sessionId
,有一个经典的解决方案,就是生成随机数,检查是否已经存在,若已存在,那么重新生成。
这种方法大概率是没问题的。
但是还是实现了一个无碰撞的生成方案。
观察到一个凭证只在短时间内有效,目前设置的是时长2min
的滑动窗口。
那么2min
之前生成的凭证,已经在Redis
中过期了,不需要再判断有没有碰撞。
可以取当下的时间戳,用2min
取模,模数恰好是6
位。
代码中目前用的是3min
,做一下滑动窗口长度的冗余。
阻止未经授权的查询
sessionId
是一个六位的数字,是不是可以暴力枚举,查询服务器所有会话的状态。
这样显然是不安全的。
在向浏览器传递sessionId
时,携带Jwt
作为auth
凭证。
只有Jwt
的负载部分与sessionId
匹配时,才允许访问。
阻止未经授权的授权
期望是有权限的用户才有权限授权。
利用SpringSecurity
,在对访问鉴权之后,会在上下文存储权限列表。
可以在下文直接取出。
如果权限列表没有我需要的权限,那么阻止授权。
Spring端设计
考虑到未来会扩展更多的服务,需要配置不同的权限,所以授权页面也做了对未来的支持。
在路径中传入申请的权限,在登录时阻止无权用户。
考虑到有权用户未来可能无权,也需要阻止。
权限维护在数据库中,请求服务的时候还是再次验证。
SetCookie传递会话凭证
Redis中也塞入了sessionId
和token
的临时绑定关系,做了二次验证。
获取会话状态
用户的凭证和浏览器的凭证格式是不一样的。
当用户查询状态时,如果是第一个用户,那么说明该用户手机扫码了,才能知道这个sessionId
并授权。
- 不需要额外的外部接口供修改二维码状态到已扫描
- 用户查询的时候,就已经扫描了。
浏览器查询时,通过凭证验证身份,必须和当前会话一致才能查询。
为NGINX提供鉴权接口
这个比较简单粗暴。
能通过SpringSecurity
,走到这一步,一定是凭证合法的。
验证当前用户的权限列表。有权则允许访问。
数据库维护权限分配
简单解耦:权限变更不需要重新编译代码。
Vue端设计
原计划是搞个导航栏,搞个弹窗的,搞几个用户管理的页面的。
然后发现现在的服务接口有点少,很少有权限操作,上号用数据库也够用。
所以撤回了导航栏和弹窗,只保留了登录界面。
个人觉得外观还是挺现代化的,登录即是授权:
弹窗中鉴权
未来的增长点之一。
创建一个全局的弹窗容器,在需要时填入组件。
容器是否显示,内部填充哪个组件,等等状态,通过pinia
维护并在组件间通信。
动画效果优化
亲测如果没有动画,会等待两秒办然后出现,比较丑陋。
让GPT生成了一个加载动画,一个转圈的圆形:
为图片加了个透明度过渡动画,图片准备完成后渐显。
小程序端设计
初始方案是TS
基础模板。
发现兼容性太差了,微信开发者工具提供的初始模板,里面好多语法已经被微信官方弃用了,顶部导航栏在不同设备上高度还不一样。
而且类型检查很差,我也不知道在微信小程序中应该定义成什么类型,是否已被弃用。
最终用的是JS+Sass
。
Canvas画圈
小程序功能比较简单。
加了个渐变的圆反馈状态。
想动态修改圆的颜色和速度,发现纯css
有点困难,小程序的语法兼容性问题有点难搞。
最后改成了canvas
画圆。
但是canvas
会强制处于最高层,无论其他组件设置多少z-index
。所以画圆的时候还要画个字母。
扫码状态验证
前文提到,首个用户扫码会自动修改状态为已扫描。
如何判断是不是当前用户,就看Jwt
负责部分是不是自己。
因为Jwt
凭证的前两个部分header
都是明文转Base64
。
从Base64
转回JSON
字符串就可以。
比较坑的是aotb
在微信开发者工具中能用,手机上不能用。
export const atobPolyfill = (base64) => {
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
var output = '';
var buffer = 0;
var bitCount = 0;
for (var i = 0; i < base64.length; i++) {
var char = base64.charAt(i);
if (char === '=') break;
buffer = (buffer << 6) | chars.indexOf(char);
bitCount += 6;
if (bitCount >= 8) {
bitCount -= 8;
output += String.fromCharCode((buffer >> bitCount) & 0xFF);
buffer &= (1 << bitCount) - 1;
}
}
return output;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2025-04-13,如有侵权请联系 cloudcommunity@tencent 删除反向代理服务权限小程序登录发布者:admin,转转请注明出处:http://www.yc00.com/web/1747718285a4685532.html
评论列表(0条)