ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

微搭低代码登录模块实战:从JWT签发到角色权限控制

微搭低代码登录模块实战:从JWT签发到角色权限控制 做低代码项目做得多了会发现一个很有意思的现象业务功能再复杂大家都有办法拆解唯独登录模块看起来平平无奇却是被轻视得最多、翻车概率也最高的环节。尤其是用微搭这类的低代码平台做MBA培训管理系统学员、教务、管理员、讲师好几类身份的人都要从同一个入口进来登录逻辑里稍微留一个漏洞后面所有页面和数据的权限控制全部白搭。这一篇是这个系列的第07篇专门讲用户登录。前面几篇把系统搭建、数据源设计、页面框架的底子打好了这篇就来解决怎么让人安全地进来、顺手知道他是谁、能干什么的问题。我会把设计思路、用户表结构、密码处理、JWT令牌生成、前端登录态管理、路由守卫这些环节全部过一遍用微搭实际能落地的做法来写适合正在用低代码平台做管理系统、或者准备从零开始做登录模块的朋友参考。需要说明的是微搭平台本身提供了一些身份认证能力但针对MBA培训系统这种需要细分角色、且要对接自有管理后台的场景我采用的是自建账号体系 自定义API生成令牌的方案。这个方案的好处是逻辑透明、权限可控后面接入企业微信、钉钉、短信登录都不会被平台绑死。1. 登录模块的设计思路与方案选型1.1 先想清楚用户登录到底在解决什么问题很多人一提登录第一反应就是搞个账号密码输入框点一下按钮能跳转就行。但在真实的管理系统里登录模块要解决的核心问题其实是三个层面。第一层是身份识别也就是确认你是谁。第二层是会话建立也就是怎么证明你刚才是谁这一步决定了用户在整个使用过程中要不要反复输入密码。第三层是权限分发也就是你是谁之后你能碰哪些东西这一点在MBA培训系统里特别关键——学员只能看自己的课程和作业教务可以管理班级和排课管理员能进系统设置讲师只能接触自己授课的内容。只做第一层的登录等于没做。如果登录之后所有页面都能访问、所有数据都能查那登录就成了一个摆设。所以设计登录模块一开始就要把登录成功之后的整条链路想清楚这比登录页面长什么样重要得多。我这次在设计方案时是把用户登录跟后续的角色权限、前端路由拦截、API数据权限绑定在一起来规划的。登录接口返回的不只是一个成功/失败标记而是一整套用户信息加凭证前端拿到这套东西后才能控制页面跳转和菜单显隐。1.2 用微搭平台能力还是自建登录体系这是做微搭登录模块时最纠结的问题。微搭本身提供了用户身份、微信登录这些能力用起来非常快后台都不用自己写。但实际评估下来我最终选择了自建账号体系的方式。原因是多方面的。首先MBA培训管理系统的用户角色划分比较细而且还需要跟一个既有的学员报名信息表、班级表做关联平台的通用登录体系很难做到这种业务耦合。其次平台内置登录的账号管理和密码策略是黑盒的出了问题排查比较麻烦。最后后续可能要从管理后台批量导入学员账号或者对接企业内部的统一身份服务自建体系显然灵活得多。不要误会我不是说微搭的平台能力不能用而是说要看场景。如果做一个面向C端用户的简单小程序、活动页面直接用平台登录能力是最快的。但是做B端管理型系统尤其是涉及多角色权限、需要定制登录策略的时候自建方案才经得起折腾。我在微搭里实现自建登录的思路是前端做一个登录页面收集账号和密码通过自定义API调用云函数在服务端校验账号密码、生成令牌并返回用户信息前端拿到这些数据后存起来后续所有请求都携带这个令牌。2. 用户数据模型与登录前置准备2.1 用户表字段设计做登录模块之前第一步不是写登录接口而是先把用户数据结构设计好。我这次在微搭的数据源里建了一张用户表字段看起来不多但每个字段都是经过考虑的。主键字段用微搭系统自带的 _id 就行。用户名我选了 phone 作为登录账号因为国内管理系统里手机号是天然的账号不需要额外生成用户名学员之间也不会撞号。即使有些用户年纪偏大没有手机号也可以用邮箱或者由管理员分配工号代替只要保证唯一性即可。密码字段存的是哈希值字段名叫 password_hash底层密码用 bcrypt 加密。为什么不直接存明文在真实项目里查数据源表时经常会发生数据库泄露明文密码一旦泄露就是连环事故用 bcrypt 加盐加密后即使数据表被导出也无法逆向出原始密码。用户信息方面我设置了 display_name 来存真实姓名用于页面展示设置了 avatar_url 存头像地址设置了 role 字段来区分配色角色可能的值是 admin、teacher、student、staff 的字符串枚举值。role 字段在后续所有权限控制中都会用到必须从一开始就规划好。另外我加了两个状态字段status 用来控制账号是否可用值为 active 或 disabled管理员可以封禁账号last_login_at 记录最近一次登录时间在查看谁登录过系统的后台功能里会用上。需要补充的是如果项目需要做登录日志审计最好再加一张用户登录日志表每次登录成功失败都记一条记录包含用户ID、登录时间、登录IP和登录来源。虽然是额外成本但等你需要排查问题时就知道这张表有多宝贵了。2.2 密码加密的落地实现密码加密这个环节我在前面提到用 bcrypt这是目前比较稳妥的密码哈希算法。它的一个关键特性是自动加盐也就是说即使用户之间密码完全相同存到数据库里的哈希串也不一样可以抵御彩虹表攻击。在微搭里落地这个方案时密码加密和校验逻辑必须放在服务端处理也就是放在自定义API背后的云函数里。前端JavaScript里有现成的bcrypt库但在浏览器端做密码加密有很大的安全隐患任何一个懂前端技术的人都可以通过调试工具篡改校验逻辑或者直接跳过登录步骤。真正可靠的加密动作应该在服务端进行。具体操作时云函数里需要引入 bcryptjs 这个依赖来解决脚本库的问题。管理员创建用户时在云函数或管理页面里调用 bcrypt.hashSync(plainPassword, saltRounds)把密文写入数据源登录时调用 bcrypt.compareSync(plainPassword, hash) 来判断输入的密码是否正确比对的结果只返回给前端一个布尔值绝对不返回哈希串本身。这里要提醒一个容易踩的坑bcrypt 的哈希结果长度和常规哈希不太一样是个60字符左右的字符串。创建数据源字段时password_hash 这个字段必须设置足够的长度上限而且类型要选短文本而不是数值。之前有人把哈希值存进一个长度不够的字段导致登录时比对失败排查了很长时间才发现是数据被截断了。2.3 角色与权限的基础配置用户表里的 role 字段是权限控制的基础。在实际开发时我不建议把权限细节写死在代码里而是先在客户或者需求方那边问清楚系统里有哪几类角色各角色分别能看哪些菜单、能操作哪些功能。在MBA培训系统里我划分了四类核心角色超级管理员负责系统配置和用户管理教务人员负责班级、课程、学员事务讲师只能看到自己名下班级和课程学员可以查看自己的课程、作业和成绩。这个划分在登录校验之后会直接决定跳转逻辑比如学员登录进去直接进入学员工作台管理员登录进去进入管理后台。在微搭前端做权限控制时我是基于登录接口返回的 role 字段在前端路由跳转之前做一个判定的后面会讲解具体是怎么做的。这里想强调的一点是不要把角色信息存在一个很容易被修改的普通参数里。设计的时候我把它作为令牌里的一个声明由服务端签发。前端可以读取和展示角色信息用于界面渲染但最终决定权的校验逻辑必须放在服务端API的鉴权环节这样即使前端被篡改也无法提升自己的权限。3. 登录流程的核心实现3.1 登录页面的可视化搭建在开始写登录逻辑之前我用微搭的可视化编辑器先搭了登录页面的前端结构。页面布局很简单居中放置一个卡片式容器上下分两块区域。第一块是Logo区和系统标题区展示了系统的名称和欢迎语。第二块是登录表单包含手机号输入框、密码输入框、一个登录按钮以及一个记住我的勾选框。在微搭的可视化编辑器里拖入输入框组件后可以设置属性。手机号输入框需要设置输入类型为手机号同时打开输入长度限制属性限制为11位并且在事件里做一些前置处理。密码输入框设置类型为密码后会显示掩码比较重要的是打开它的允许输入控制属性不勾选的话默认情况下表单校验会比较宽松。在表单下面我为按钮设置了加载中状态属性这个细节虽然不起眼但实际体验差别非常大。登录请求通常会花几百毫秒到几秒如果不给用户反馈用户会感觉像卡住了连续点击多次登录按钮就会产生大量重复请求。虽然服务端可以加防重放判断但对用户来说体验很糟。我用微搭的事件绑定功能为登录按钮添加了点击事件事件动作选择调用自定义API。在调用前先在按钮的加载状态里设置一个变量控制点击后置为true请求完成后置为false。整个过程不需要手写太多代码但在可视化编辑器里把这些逻辑串起来时需要理清事件触发的顺序。3.2 自定义API与云函数里的登录逻辑登录页面的前端只是入口真正的逻辑还是在服务端。我在微搭的自定义API里创建了一个登录接口路径类似 /api/login接收参数就是手机号和密码。选择用云函数来实现这个API的逻辑因为需要在服务端做密码比对、生成令牌这些动作这些是纯前端做不安全的。我在云函数里写了一段伪代码整个流程整理出来大概是这样的校验参数是否完整和格式是否正确手机号是否符合11位规则、密码是否为空不合法直接返回参数错误。用手机号在数据源里查询对应用户查不到就返回该账号不存在。查到后先看 status 字段如果账号被停用则返回账号已停用请联系管理员。接下来用 bcrypt.compareSync 比对密码哈希不一致则返回密码错误。比对通过后更新该用户的 last_login_at 字段为当前时间。最后生成一个JWT令牌里面包含 userId、role、expiresIn 等字段并连同用户基本信息一起返回给前端。这里有个设计细节值得多说两句。查询用户时不只是查一个手机号我会先在密码比对前就检查账号状态。很多系统习惯先比对密码再查状态但这样做会导致被停用的账号也能感知到密码正确给安全日志留下了不必要的隐患。以前者优先的话被停用账号的登录尝试会被挡得更早。3.3 更新用户登录信息并生成JWT令牌这一部分就是热搜词里更新用户登录信息并生成返回JWT令牌这句话的落地方案。JWTJSON Web Token本质上是一个经过签名的JSON字符串把用户身份信息放到令牌里服务端通过签名校验令牌的合法性前端在每次请求时带上它服务端就可以确认请求者的身份。在实际项目中我使用的JWT结构分成三个部分。头部指明签名算法是HS256载荷部分包含用户ID、角色和过期时间签名则用服务端密钥对前两部分进行HMAC加密。这样的令牌结构在微搭的云函数里可以用 jsonwebtoken 这个依赖库方便地生成。生成令牌的时候我设定了一个重要参数过期时间。对于这个MBA培训管理系统我给令牌设定的有效期是12小时也就是用户登录一次一天之内不用重复登录。这个长度对后台管理系统来说相对合适既不会频繁打断用户操作也不会让长期不用的令牌在过期后继续生效。除了令牌本身登录接口还返回了用户的角色信息、昵称、头像这些前端展示时需要的数据。有经验的读者应该会注意到这些数据在JWT载荷里其实已经有了为什么还要单独返回一份因为在业务逻辑中前端展示需要的是实时的用户资料比如头像和昵称可能在用户修改个人资料后变化。如果只用JWT里的旧数据展示就会不准确。所以接口返回的数据是最新资料 有效令牌的组合。为了后续的权限校验我把云函数的代码设计成可以复用的鉴权方法。需要鉴权的API在接受请求时可以先去解析请求头里 Authorization 字段里的JWT令牌验证签名和过期时间如果通过就取出 userId 和 role再执行具体的业务逻辑。这套鉴权方法在后面的订单、选课、成绩等API里都通用。4. 登录状态管理与权限控制实战4.1 前端登录态的存储与读取登录接口返回的JWT令牌和用户信息前端需要一个合适的存储位置来管理。在微搭的应用开发中我通常会把令牌存在微搭的全局变量里同时做一份持久化存储到本地存储中两者配合使用。全局变量的好处是任意页面的代码块都可以直接访问和修改不需要在每个页面之间传参。在应用加载的时候我定义了一个全局变量对象 applicationState里面包含 currentUser、accessToken、role 等字段。登录成功后把这些字段赋值进去。本地存储的作用是解决刷新页面的问题。单页应用里的全局变量在页面刷新后会丢失如果用户正在操作系统并意外刷新直接跳回首页需要重新登录体验很差。我在登录成功时把令牌也同步到 localStorage 里在应用启动的最早时机去读取如果有有效令牌就自动恢复登录态。这里有一个容易做错的细节存到本地存储的值不要带敏感的用户身份证号、手机号明文等信息。本地存储是一个基本透明的地方任何运行环境中的脚本都有办法读到你放在这里的值核心的令牌虽然短时间内有效但是如果泄露攻击者就可以用这些数据去发起请求。所以我在本地存储里只保存令牌不保存密码也不保存完整的用户资料用户详情在需要时还是从服务端获取。4.2 基于角色的页面跳转与菜单权限登录成功后前端需要根据角色来决定用户往哪儿跳。做法不复杂在登录成功回调里通过 JavaScript 代码判断全局变量中的 role 字段不同的角色分配到不同的工作台路径。学员角色直接跳转到学员工作台页面管理员跳转到管理后台首页。这里要注意的是路由跳转不能只做一次否则用户手动在浏览器地址栏输入其他角色的页面地址照样能打开。所以我还做了一个路由守卫的逻辑在页面加载前先判断当前是否已登录、令牌是否有效、以及当前页面要求的角色是否匹配。微搭的页面跳转控制可以通过在页面生命周期里写代码来判断。我会在MBA培训系统的每个受保护页面的事件流里先检查全局变量里的登录状态和角色如果不满足条件就直接重定向到登录页并携带一个参数记录你原本想访问的地址方便登录完成后回到原页面。菜单权限的处理也类似。系统左侧菜单栏根据当前用户的 role 渲染不同的菜单项这一层是纯前端的交互优化真正的数据安全还是要靠服务端API鉴权来兜底。但至少从体验上学员登录后看不到用户管理这个菜单入口这就已经起到了很好的引导作用。4.3 API请求的统一鉴权封装登录态的携带是一个偏基础但必须统一处理的工作。我建议把微搭前端发请求的动作全部封装成一个统一的请求函数在函数内部自动在请求头中加入 Authorization: Bearer 。做这件事的原因是如果每次请求都手动写一行代码去带令牌就必然有人会漏写、写错一旦漏写服务端就无法识别请求者身份轻则功能报错重则安全漏洞。统一请求函数后所有页面调接口只需要关注业务参数鉴权是自动完成的。我封装了 request 方法统一处理了四个环节请求前自动引入令牌到请求头请求完成后判断返回的HTTP状态码如果返回401则代表令牌已过期或无效此时清除本地登录态并跳转到登录页其他错误则显示一个Toast提示。这里要特别说一下401的全局处理。很多开发者在最初处理时只在登录接口里处理错误在业务接口里就忽略了。结果就是用户坐在电脑前聊天聊到一半再点一个功能页面突然没反应谁也不明白发生了什么。统一处理401之后用户在令牌过期后的第一次操作就会直接跳回登录页体验顺畅得多也不容易出奇怪的问题。另外我在封装请求函数时还会加一个判断当请求返回的令牌快过期时主动续期。这里的实现方案是JWT令牌满足刷新条件剩余有效期低于总有效期的50%时前端调用一个刷新令牌接口拿到新的令牌并更新本地存储和全局变量。可能有人会问这个过程有点复杂其实复杂是为了体验的顺滑否则系统在使用高峰期会频繁要求用户重新登录。5. 常见问题与排查技巧实录5.1 登录失败类问题排查做登录模块的过程中我遇到过不少典型的登录失败情况这里整理一个实际项目的排查清单按频率排序。第一条是提示账号不存在但用户明明输入的是正确手机号。原因通常是用户在前台注册时手机号被存成另一个格式比如数据库里混入了 86 前缀或者是短信验证码注册时手机号填错了。排查时先去数据源里查改用户的 phone 字段实际存的值。处理办法注册时统一做手机号格式化把 86 之类的国家和区号统一去掉只保留11位数字。第二条是提示密码错误但密码确定没记错。排查步骤是先确认用户是否走过了忘记密码流程但一直没用同时看看密码哈希是不是在创建用户时被截断处理过。如果用户密码经过了两次哈希比如在客户端哈希过一次服务端又哈希一次也会导致比对失败。所以密码流程一定要统一原始密码只在客户端明文传输到服务端服务端做唯一一次哈希。第三条是账号已停用这种情况说明 status 字段被改过。排查时需要确认是不是管理员通过管理后台误操作。我会在管理后台的操作记录里加上状态变更的日志这样至少可以追溯到是谁改的。5.2 登录成功但跳转异常令牌签发成功用户资料也拿到了但前端页面没有按照角色跳转到正确的工作台这种情况通常出在前端逻辑上。最常见的坑是角色判断分支写错了。比如登录接口返回的 role 字段的值后端定义的是 admin前端的判断条件写的是 role administrator对不上号自然进错界面。我在排这种问题时会先打开浏览器的开发者工具在控制台里打印一下登录成功后全局变量的实际值一眼就能看出来。还有一个容易被忽略的问题是路由守卫的执行顺序。加载页面时路由守卫先启动了但全局变量里的用户信息还没有从本地存储读出来并赋值完成守卫以为是未登录状态就先把用户踢回登录页了。避免的办法是把读取本地存储、恢复全局变量这个动作放到应用启动阶段放在路由判断之前去完成。另外有些浏览器环境对 localStorage 的访问有时机限制在某些情况下读取会失败导致会话丢失可以在读取的时候做一个判空容错处理别让应用直接崩溃。5.3 令牌过期与会话丢失问题令牌过期在整个登录体系里是最容易出问题的环节我在前面已经提到了刷新机制。这里想聊一下刷新失败时容易出现的坑。设计的刷新接口需要拿着旧的、未过期的令牌去换一个新的。但我在实际测试中发现如果用户使用的令牌恰好只剩几秒有效期前端发出刷新请求时服务端直接就判定令牌无效导致刷新失败、用户被强制登出。处理办法是前端在决定是否刷新时不要等到最后一刻而是在令牌剩余有效期低于总时长的三分之一时就去触发刷新。还有一种情况是用户有多个标签页同时在打开系统。一个标签页执行了刷新操作把旧令牌作废了或者更新了本地存储但另一个标签页里的全局变量还是旧令牌值。这时第二个标签页的请求会带着旧令牌去访问服务端就返回401了。处理办法是把令牌过期判断做得宽松一点服务端不只认一个令牌而是接受当前有效期的令牌两个版本旧令牌在过期后的一个宽限期内还可以用于鉴别。这个方案听起来复杂但实际做下来效果非常稳定。如果你做的系统没有多标签页的场景也可以简化掉但多标签场景在Web端管理系统中太常见了面试官问到的时候这个点会很加分。5.4 环境切换后的登录异常微搭低代码开发的一个很大的特点是不同环境开发、测试、生产有不同的数据源和服务配置。但如果环境切换处理不当登录态会串环境。我在开发过程中遇到过这样的现象测试环境登录成功切到生产环境预览时也会自动带上测试环境的令牌请求打到生产环境的接口上服务端校验不过页面就进入了一种看似登录了但什么都加载不出来的状态。解决办法是在本地存储的键名上做环境后缀。比如测试环境用 token_dev 作为键名生产环境用 token_prod。不同环境读到的值互不干扰就不会串了。另外切换环境时最好在应用的初始化阶段主动检测并清除非当前环境的登录缓存。如果项目里还有微信小程序端的入口就要额外注意小程序端和Web端的本地存储本来就互相隔离不必做跨端同步。但业务上可能会遇到Web端登录了、小程序端还需要重新登录的正常情况要让客户明白这个是符合预期的不要以为是bug。写在最后做微搭低代码MBA培训管理系统的这段经历让我越来越认同一个观点低代码平台解决的是开发效率的问题但系统设计上该有的严谨一步都不能少。登录模块在功能上只是一个小环节它牵扯出来的数据模型设计、令牌管理、角色权限、异常处理桩桩件件都是真刀真枪的功夫。如果你正在做一个类似的低代码管理系统我的建议是先把登录相关的接口测试用例写好尤其要覆盖密码错误、账号停用、令牌过期、角色无权访问这些边界场景。不要因为登录页看着简单就从简处理等到上线后学员集体反映登不进去前一个账号没退出再登录就出问题的时候返工的成本会高得多。最后分享一个小技巧在微搭里调试登录逻辑时尽量在云函数里多打印一些阶段性的日志比如参数校验通过用户查询成功密码比对结果令牌签发成功配合API日志中心可以非常快地定位到是哪一个环节出了问题。这个小习惯在我排查线上问题的时候帮了大忙。
返回列表