资讯动态

ROS WEB认证全链路:热点网关、登录页与RADIUS对接实践

发布时间:2026/10/27 4:58:22 来源:尧图企业网站定制
简介面向RouterOS网络管理员的Web认证配置素材包聚焦公共场所无线接入场景下的热点登录页定制与认证流程落地。内容围绕热点认证的登录、状态、重定向、错误提示与登出等页面组织并配有界面切图、样式表、前端脚本及WISPAccessGatewayParam.xsd校验定义可辅助理解认证策略、页面跳转与终端兼容性处理。压缩包共71个文件以HTML页面和JPG、PNG、GIF界面图片为主另有CSS、JS、XSD及TXT说明文件整体约95KB目录按热点模板与多语言分支划分便于按模块查阅替换。已有308人学习适合需要搭建或改造ROS热点认证页、排查登录失败与页面显示问题的网络管理员参考也能为品牌化登录页和认证后权限提示提供可复用的结构基础。1. 一次弹窗背后ROS WEB认证到底在认谁刚接访客 WiFi 那会儿我以为 ROS WEB认证就是换个登录页图片。到现场才发现用户点开浏览器弹不弹页、弹出来能不能提交、提交完跳不跳得回去每一步都卡在不同角色手里。ROS WEB认证把热点网关、登录页和后端账号系统串成一条链路网关负责拦住未认证流量、把登录页送到用户面前登录页负责收账号、回传结果后端负责判断这个账号能上线多久、跑多快、能几台设备同时在线。它适合园区、酒店、展厅、公寓这类访客多、设备杂、要按账号限速和计时的场景如果你只是给办公室加一个统一 WiFi 密码这套东西反而重了。下面按这条链路拆能直接抄的配置和代码我都写出来。2. 认证链路先立住热点、登录页、后端的三段式分工2.1 从连上 WiFi 到能上网中间隔了几个跳终端连上 SSID、拿到 DHCP 地址之后其实还没有任何转发权限。此时热点会把该终端的所有 HTTP 请求拦下来用 dst-nat 把首包重定向到热点网关的 80 端口返回 login.html。这个“先拦截、再放行”的机制就是整条认证链路的地基。一次完整的登录大致是这么走的跳参与方发生什么卡住时的表现1终端与热点关联、DHCP 拿到热点网段地址连上就断或拿到 169.254 自分配地址2热点拦截未认证流量返回登录页浏览器不弹页或弹了空白页3登录页与热点表单 POST 到登录地址携带账号密码点了登录没反应或提示密码错误4热点与账号源本地用户表校验、或转发 RADIUS一直转圈最后跳回登录页5热点写 active 表返回跳转页登录成功但打不开任何网页6热点与计费会话开始计时、计流量、发计费报文时长对不上、限速不生效这六跳里任何一环出问题用户看到的现象都是“上不了网”但你要排查的位置完全不同。所以现场排查的第一句话不应该是“密码对不对”而是“他现在停在第四跳还是第五跳”。把这句话问清楚能省下至少半小时。会话建立之后热点会持续维护 active 表记录账号、IP、MAC、上线时间、收发字节数。会话到期、被管理员移除、或者 idle 超时用户就会被踢回登录页。如果接了 RADIUS还会在这一头一尾各发一次 Accounting-Request用来做计费对账。这一段是很多人忽略的地方登录能不能通是显性问题计费准不准是隐性问题后者往往一个月后才被发现。2.2 为什么选 WEB 认证三种接入方式的边界同样是“让用户能上网”接入方式的选择直接决定了后面半年的运维工作量。常见的三种做法能力边界差别很大维度WEB 认证802.1X预共享密钥账号粒度一人一账号可计时计流量一人一证书或域账号全员一个密码终端兼容浏览器即可无需装客户端需 supplicant老设备常失败全兼容访客开通效率扫码或短信即可需提前下发证书发密码即可但收不回可运营性强能限速、到期、踢线强但依赖证书体系弱只能靠 MAC 黑白名单部署依赖热点网关 认证页 账号源证书体系 RADIUS 域控无典型场景访客、酒店、公寓、展厅固定办公、密级较高的内网临时小范围选型的判断很朴素如果账号要能收回、要能按人限速、要能算出谁用了多少那基本只有 WEB 认证和 802.1X 两条路802.1X 的前提是你有一套能发证书或能对接域账号的体系很多现场并不具备于是 WEB 认证就成了默认答案。但 WEB 认证的成本不在技术上在账号生命周期上。开号、续期、停用、离职回收、并发控制这些流程如果没有落进一个后台最后就会变成一张 Excel 表和一堆口头申请。选它之前先把这块想清楚比研究登录页配色重要得多。2.3 先搭一个最小闭环网关、账号、登录页跑通我做这类项目从来不上来就接后端先在热点本机把“能弹页、能登录、能跳回”跑通再谈对接。最小闭环的配置大概是这样# 热点网关与地址池网段按现场规划改别和办公网重叠 /ip pool add namehs-pool ranges10.10.0.10-10.10.0.200 /ip address add address10.10.0.1/24 interfacebridge-hotspot # 热点模板登录方式、登录页域名、认证页目录 /ip hotspot profile add namehs-prof hotspot-address10.10.0.1 \ dns-namewifi.example.internal \ login-byhttp-pap,http-chap \ html-directoryhotspot # 把接口挂上热点绑定地址池 /ip hotspot add namehs-hotspot interfacebridge-hotspot \ address-poolhs-pool profilehs-prof disabledno # 账号模板限速与并发先给保守值后面按业务调 /ip hotspot user profile add namestd rate-limit2M/1M shared-users2 /ip hotspot user add nameguest001 passwordChangeMe#01 profilestd几个参数值得单独说一下。hotspot-address是用户浏览器实际访问到的登录地址必须和网关地址一致写错了会出现“页面打不开、但能 ping 通”的怪现象。dns-name是登录页自己的域名热点网段的客户端要能解析到它解析不了就只能靠 IP 访问后面做 HTTPS 跳转会很别扭。login-by先留 PAP 和 CHAP 两种是为了在接自家后端时能快速切换最后再定死其中一种。html-directory指向热点文件系统里的目录名你自定义的登录页要传到同名目录下才会生效。rate-limit和shared-users这两个值建议一开始就低一点。限速先给 2M/1M并发先给 2等业务方明确说不够再往上加。反过来做——先给大、再往下压——用户的投诉会集中在“为什么突然变慢了”这件事上解释成本高得多。提示rate-limit的写法是上行/下行还是下行/上行不同固件版本和不同人的习惯说法容易打架上线前拿两台手机在内外网各测一次速把方向钉死在文档里别凭记忆。这一步跑通之后用手机连一次热点能弹页、能提交、能跳回原来想访问的地址说明链路是通的。接下来才轮到把账号源换成后端、把登录页换成自己的样式。顺序反了后面任何一个环节出问题你都要同时在三个地方怀疑人生。3. 认证页工程化模板变量、表单字段与后端对接3.1 login.html 的骨架和几个必须认识的变量热点返回给浏览器的登录页本质是一个带占位符的 HTML 模板。热点在把页面吐出去之前会先做一次变量替换把这些$(...)换成当前会话的真实值。所以你的页面随便美化但变量名一个都不能改。变量含义常见坑$(link-login-only)只做登录的提交地址误写成$(link-login)会把原始目标一起带上容易绕圈$(link-orig)用户被拦截之前想访问的地址为空时没有回退值跳转会失败$(error)上一次提交的错误信息不做映射用户看到一长串英文报错$(chap-id)/$(chap-challenge)CHAP 挑战参数接了自家后端还对不上认证必挂$(mac)终端 MAC模板里没注入做设备绑定时取到空值$(hostname)热点主机名多热点部署时不区分日志里追不到是哪台一个能用的骨架长这样!-- login.html样式随便换$(...) 变量名和表单字段名不能动 -- !DOCTYPE html html langzh-CN head meta charsetutf-8 meta nameviewport contentwidthdevice-width, initial-scale1 title访客上网认证/title /head body !-- action 必须是 link-login-only字段名必须是 username/password/dst/popup -- form idloginForm action$(link-login-only) methodpost input typehidden namedst value$(link-orig) input typehidden namepopup valuetrue div idmsg$(if error)$(error)$(endif)/div input idusername nameusername placeholder账号 autocompleteoff input idpassword namepassword typepassword placeholder密码 button typesubmit登录/button /form script // 把热点渲染好的 MAC 存下来走自家后端时带上做设备绑定 window.macAddr $(mac); /script /body /htmldst这个隐藏字段决定了登录成功后跳回哪里值直接取$(link-orig)。如果用户是直接输 IP 打开浏览器、没有原始目标$(link-orig)会是空串这时候最好在提交前用一个默认地址兜底否则登录成功后页面会停在一个空白跳转上。popup是给弹窗式登录留的开关保留true就行。$(error)这块很容易被糊弄过去。模板里直接输出用户看到的是invalid username or password这种英文原文体验很差。更稳的做法是在页面里做一次映射把几种常见错误翻成中文提示同时把原始值打到前端日志里方便排查。3.2 两条对接路线表单直达热点还是先过自家后端账号校验放在哪一端决定了登录页要写多少逻辑。现场常见两条路线。路线一表单直达热点。热点自己去查本地用户表或者 RADIUS登录页只负责收输入和展示错误。这条路线的优点是链路短、故障点少登录耗时基本就是一次内网请求缺点是你的业务系统必须和热点或 RADIUS 共享账号数据开号、改策略要同步。路线二先过自家后端。页面先把账号发给业务后端后端查有效期、余额、黑名单通过了再提交给热点。这条路线的灵活性高能接短信验证、扫码、第三方账号但代价是热点这边必须配置成 PAP 方式因为自家后端拿不到 CHAP 挑战的上下文加密对不上。而且多了一次网络往返弱网下登录耗时能翻倍。// 路线二的前端逻辑先问自家后端要一个准入结论再交给热点校验 async function doLogin(ev) { ev.preventDefault(); const user document.querySelector(#username).value.trim(); const pass document.querySelector(#password).value; // 1) 业务后端校验账号是否存在、是否在有效期、是否已被拉黑 const resp await fetch(/api/hotspot/precheck, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ username: user, password: pass, mac: window.macAddr }) }); const res await resp.json(); if (res.code ! 0) { // 后端返回非 0 时直接展示原因不提交给热点 document.querySelector(#msg).innerText res.msg; return; } // 2) 准入通过把账号密码交给热点由热点完成真正的会话建立 document.querySelector(#loginForm).submit(); }这段代码里有两个参数是业务侧最容易漏的。mac用来做一账号一设备的绑定值来自模板注入的window.macAddr没注入就是undefined后端会一律当成新设备绑定等于没做。code的约定要固定下来0 表示放行非 0 表示拒绝并带msg前端不再做二次判断避免两边逻辑各写一套、最后对不上。路线二还有一个隐性成本页面上多了一个 fetch就意味着登录页依赖你自己域名的接口可用性。这个接口挂了所有用户都登不上而热点本身是好的。所以要么给这个接口做本地缓存兜底要么在页面上加一个“直接登录”的备用入口。3.3 跳转回传、错误码与断线重认证登录成功之后热点返回的是redirect.html由它把用户带回dst指向的地址。这一步的体验细节比想象中重要如果跳转页是空白用户会以为没登上反复点登录结果触发并发限制被锁。错误码建议做成一张固定的映射表放在前端或者后端统一处理错误值含义处理建议invalid username or password账号或密码错误累计 5 次锁 10 分钟避免撞库RADIUS server is not respondingRADIUS 不可达查密钥、1812 端口、路由already authorized该账号已在线引导去注销页或提示并发超限hotspot user profile not found用户模板不存在检查账号绑定的 profile 名断线重认证是另一块容易出问题的地方。会话超时后热点会把用户踢回登录页$(link-orig)通常还保留着原来的目标地址体验能接上。但如果用户在会话期间换了网络、或者手机休眠唤醒终端可能重新走一遍探测流程这时候如果探测请求被 walled-garden 放行了设备会认为“已经有网”反而不弹登录页用户就会卡在一个能连不能用的状态。这种情况在 5.1 节里还会展开。4. 账号与策略下发库表、限速参数、会话清理4.1 账号先落库表结构与校验链路热点本机加几十个账号还能手工维护上百个就必须有自己的库表了。开号、续期、停用、查询在线状态这些动作都需要一个地方存。一个够用的表结构大概是这样-- 热点账号主表一个账号一行策略按 profile 归组 CREATE TABLE hotspot_account ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(64) NOT NULL UNIQUE, pass_hash VARCHAR(255) NOT NULL, -- 存 bcrypt不存明文 profile VARCHAR(32) NOT NULL DEFAULT std, rate_rx INT NOT NULL DEFAULT 2048, -- 单位 kbps rate_tx INT NOT NULL DEFAULT 1024, shared_max TINYINT NOT NULL DEFAULT 1, -- 同账号并发终端数 expire_at DATETIME NULL, -- 到期时间NULL 表示不过期 status TINYINT NOT NULL DEFAULT 1, -- 1 正常 0 停用 created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ); -- 会话流水每次登录写一条用于对账和投诉追溯 CREATE TABLE hotspot_session ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(64) NOT NULL, mac VARCHAR(32) NOT NULL, ip VARCHAR(46) NOT NULL, started_at DATETIME NOT NULL, ended_at DATETIME NULL, bytes_in BIGINT NOT NULL DEFAULT 0, bytes_out BIGINT NOT NULL DEFAULT 0, INDEX idx_user_time (username, started_at) );几个字段的取舍值得说明。pass_hash一定存哈希别图省事存明文认证页的密码在 PAP 模式下本来就是明文提交的落到库里再存明文风险叠加。profile存的是热点用户模板名账号的策略实际由模板决定表里的rate_rx/rate_tx只是业务侧留存的期望值方便展示和迁移真正生效还是看模板。expire_at用 NULL 表示不过期比用 9999 年这种魔法值干净SQL 判断也更直接。校验链路走起来是这样运营在后台开号记录写入主表登录时后端按username查主表比对pass_hash、判断status是否正常、expire_at是否已过再查一次当前在线数是否超过shared_max全部通过才放行。放行之后写一条会话流水ended_at留空等下线时再补。注意不要把在线数判断放在热点侧就完事热点只认shared-users它不知道你的业务规则。两个地方都要判否则会出现“后端认为超限拒绝、热点认为没超限还把人踢了”的互相打架。4.2 限速、并发、时长参数怎么定不返工策略参数是最容易引起返工的部分因为它和用户预期直接相关。我一般按这张表先定一版再根据投诉调整参数配置位置影响参考值rate-limit用户模板单账号上下行带宽访客 2M/1M员工 10M/10Mshared-users用户模板同账号并发终端数访客 12员工 3session-timeout用户模板单次在线时长访客 4h员工 12hidle-timeout热点服务空闲多久踢下线1015minkeepalive-timeout热点模板保活探测间隔保持默认弱网别调小rate-limit的单位是 kbps2M/1M就是 2048/1024。burst 相关的参数先不碰默认值足够应付绝大多数访客场景等真有大量投诉再单独调否则你很难分清是限速策略的问题还是无线信号的问题。shared-users设成 1 是最省事也最容易挨骂的配置。现在的用户手机、平板、笔记本三件套很常见一个账号只允许一台设备后台会收到大量“登一个掉一个”的反馈。访客账号给 2员工账号给 3是比较稳的折中。idle-timeout这一项很多人调得过小觉得能省资源。实际上手机在锁屏时会停止流量如果 idle本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑