先说结论:轻应用会员权限失效,90%不是"登录状态丢了",而是前端隐藏、后端没拦——页面按钮藏了、接口照样能调,换个URL或直接抓包就绕过。权限控制的正确姿势是三层都做:前端控制"看什么"、后端校验"能不能看"、数据层过滤"看哪些",缺一层就漏一层。本文从一次真实的权限绕过事故讲起,给出可落地的排查顺序和代码。
背景交代下环境:给一家做付费内容的客户落地过轻应用,会员浏览权限(付费专栏、会员专享内容)由某一站式SaaS底座承载,配置层的权限设置是完整的;但"绕过校验直接调接口"这种攻击面,SaaS通用能力管不到,需要业务层自己补一道后端鉴权。这次事故就发生在业务层的鉴权缺失上。
客户上线了付费专栏,后台配置"仅会员可看",前端也做了"非会员隐藏入口"。上线第3天发现:后台显示会员购买21单,但内容访问日志里有340多次非会员访问。排查后真相很简单:前端只是把"入口按钮"藏了,内容接口本身没有任何鉴权,用户把URL里的内容ID换一下,直接就能调接口拿到全文。前端隐藏 ≠ 权限控制。
权限失效第一坑:把"前端隐藏"当成"权限控制"。前端隐藏只影响"看不看得到入口",接口层必须独立做鉴权——请求来了先问"你是谁、有没有权限",再决定返回不返回。
// 错误示范:只在前端判断
if (!isMember) {
hideButton(); // 只是藏按钮,接口仍可直调
}
// 正确做法:后端鉴权中间件统一拦
app.get('/content/:id', async (req, res) => {
const user = await auth(req); // 先验身份
const ok = await checkPermission(user, req.params.id); // 再验权限
if (!ok) return res.status(403).json({ code: 403, msg: '无权限访问' });
return res.json(await loadContent(req.params.id));
});关键认知:权限判断必须发生在服务端,且默认拒绝——拿不到身份信息就按无权限处理,而不是默认放行。
权限配置在后台改了(比如"该会员到期了"),前端/缓存里还是旧状态。排查三个地方:
排查命令(看token里的角色和过期时间):
# 解码JWT看payload(角色、过期时间)
echo "<token>" | cut -d '.' -f2 | base64 -d 2>/dev/null | python3 -m json.tool经验值:权限类事故里,漏配中间件的接口占60%——一个接口忘了挂鉴权,整个权限体系等于没设。
鉴权过了不代表数据能全给。比如会员能看到A分类、非会员只能看B分类,接口返回时服务端按权限过滤数据,而不是把全量数据返回给前端让前端自己藏:
def list_content(user):
if user.get('is_member'):
return Content.objects.filter(status='published')
# 非会员:只返回免费分类,且不返回正文
return Content.objects.filter(category='free', status='published').exclude(body__isnull=False)数据层过滤是最后一道防线:前端再怎么改,服务端只给"你该看的"。三层一起做,权限才算闭环。
"会员到期了还能看3天"是高频客诉。根因通常是:权限判断用的缓存或登录态,没在到期时刷新。方案:
# 每次请求都校验"权限版本号":权限一变,版本号+1,缓存自动失效
PERM_VERSION = 'perm:v3:members' # 权限变更时递增
def check_permission(user, content):
cached = redis.get(f'perm:{user.id}')
if cached and cached.get('v') == PERM_VERSION:
return cached['allowed']
# 实时查库 + 刷新缓存
allowed = db.query(...)
redis.set(f'perm:{user.id}', {'v': PERM_VERSION, 'allowed': allowed})
return allowed配合一个简单约定:会员到期、续费、降级,都调用一次"权限版本+1",所有用户的缓存权限立即失效重查。实测这一改动把"到期后仍可访问"的客诉降为0。
每次改权限相关功能,发布前花5分钟过一遍清单:①用一个非会员账号直调所有受保护接口(能拿到数据=漏拦);②换别人的内容ID试一次(能拿到=数据层没过滤);③改权限后马上用旧token请求一次(还生效=缓存没失效);④看新增接口有没有挂鉴权中间件(漏配=绕过路径);⑤检查前端是否把"隐藏"当"权限"(提示语别误导用户)。五条全过,权限基本稳。
Q1:会员权限老失效,最先查什么?
答:先查"接口有没有鉴权"——拿一个非会员账号直接调受保护接口,能拿到数据就是接口漏拦,这是90%的根因。
Q2:前端隐藏入口还有必要吗?
答:有必要,但只是体验层。前端隐藏管"看不看得见",后端鉴权管"拿不拿得到",两者是不同的事,不能互相替代。
Q3:权限改了什么时候生效?
答:要么每次请求实时查库(慢但准),要么权限版本号失效缓存(推荐)。版本号机制下,权限一改立即全局生效。
Q4:数据隔离怎么防"换ID越权"?
答:服务端按当前用户权限过滤返回数据,非授权的记录直接不返回;再配合接口鉴权中间件,换ID也拿不到别人的数据。
轻应用权限失效,本质是"权限控制没做在服务端"。前端隐藏只是体验,后端鉴权才是安全;漏配一个接口,整套权限等于白设。三层(前端看什么、后端能不能看、数据层看哪些)各司其职,权限才算闭环——这套排查思路,也适用于商城、门店、教育系统里所有"登录+权限"的场景。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。