简介:开源实现的Portal协议服务端程序,基于Java技术栈编写,面向网络运维人员与认证系统二次开发者。支持华为、H3C、锐捷、爱快等主流设备,覆盖标准Portal、Portal V1/V2、CMCC等协议,兼容PAP/CHAP认证,提供一键认证、系统接入用户认证、外部Radius认证、微信认证、短信认证、动态密码等多样化接入方式,适合园区网、酒店、校园网等场景部署。整个压缩包约52MB,共1477个文件,其中class与jar为编译后的核心逻辑与依赖库,jsp/js/css构成管理界面,xml/properties为配置项,另有sql脚本、Tomcat启动/关闭脚本、shell/bat运维脚本及APK客户端,目录结构完整,便于直接部署或改造。框架采用SpringMVC、Spring、Mybatis、Shiro、Ehcache、quartz、jersey等主流组件,代码组织清晰,可作为学习Portal协议实现与Java Web整合开发的参考。目前已有1349人学习下载,借助该源码可快速搭建测试环境,理解认证报文交互流程,并基于标准接口扩展第三方认证,适合需要对接设备认证或搭建自研认证平台的技术人员。 做公共场所WiFi运营的人,对OpenPortalServer这个名字应该不陌生。作为一个开源认证门户系统,它的核心任务很明确:把"谁都能连"的WiFi变成"只有授权用户才能用"的WiFi。用户连上热点后,网关会把浏览器的HTTP请求强制重定向到门户页面,用户在页面上输入账号密码或兑换码,系统校验通过后才正式放行,流量放行的同时还能附带完成限速、时长统计、在线用户管理等运营动作。我手上这套V3.3.5.6 Stable版本发布在2016年1月16日,算得上当年这个项目稳定分支里的一个代表作。两年前接手一家小型园区WiFi改造项目时,我拿它做了整套认证核心,从测试到全量上线再到日常维护,过程里走了不少弯路,也沉淀了不少经验。这篇文档就把我从零部署到稳定运行的完整过程记录下来,给准备做公共WiFi认证的朋友一个可参照的实操样本。
1. 认证门户在WiFi网络里的定位:为什么需要这么一套系统
1.1 一条完整的公共WiFi认证链路拆解
先还原一个最典型的现场。客人走进咖啡馆,手机连上名为Cafe-FreeWiFi的热点,此时设备从DHCP拿到的只是一个内网IP,任何访问外网的请求都会被网关截住。他打开浏览器随便输入一个网址,页面立刻跳转到一个登录门户,上面写着"请输入小票上的上网验证码"。客人在收银台结账后获得一张印有8位验证码的小票,输入验证码点击连接,几秒后跳转到"连接成功,欢迎使用"的页面,这时再去刷视频、看网页都已经畅通无阻。
这条链路拆开来看包含几个环节:DHCP分配地址、DNS解析、HTTP请求触发重定向、认证页展示、凭证校验、网关放行、会话记账。OpenPortalServer承担的就是其中"认证页展示+凭证校验+用户管理+会话记账"这一整块,也就是从用户看到登录页开始,到他被正式放行之间的所有逻辑。网关设备则负责最前端的重定向和最末端的流量放行,两者通过一套约定好的UAM(Universal Access Method)协议交互,OpenPortalServer这边处理好之后,通过后台回调接口通知网关"这个用户已经被放行了"。
如果用打比方的方式理解,网关像是商场门口的闸机,OpenPortalServer就是闸机后面的核验台。闸机只管拦住人,把每个人送到核验台面前;核验台检查身份、登记信息、决定谁可以进、谁要延长时间,然后通过对讲机通知闸机打开通道。这个分工保证了整个系统可以横向扩展——核验台效率不够了,可以单独升级,不需要动闸机;反过来也一样。
1.2 为什么选OpenPortalServer而不是商用或自研方案
做选型的时候,我实际上对比过三类方案。第一类是商用认证平台,按AP数量或者用户数收费,功能确实全,但小园区的预算根本吃不住那个授权费用。第二类是自己用PHP或Python写一个简易门户,做个数据库账号校验不难,但要做到兑换码批量生成、定时段限速、RADIUS记账、在线踢人这一层,开发工作量就不是一周两周能收尾的了。第三类就是OpenPortalServer这类成熟开源项目,优势在于把运营场景里高频用到的功能都做成了现成模块,社区里也有大量同类型部署案例可以参考。
具体到V3.3.5.6 Stable这个版本,我在选型时看中了几个点:一是它和Coova Chilli这类网关组件的兼容性经过社区大量验证,对接文档齐全;二是管理后台的界面虽然朴素,但功能分区清晰,日常操作不需要依赖命令行;三是它支持本地账号和外部RADIUS双通道认证,意味着后期如果要接入更复杂的计费或统一认证平台,不需要推倒重来。后来的运维实践也证明,这个版本虽然发布时间早,但该有的功能一个不少,稳定性和资源占用表现都不错。
2. 部署前的环境准备与选型清单
2.1 硬件配置与软件栈选择
OpenPortalServer本质上是一套PHP+MySQL的Web应用,因此它不挑硬件。以我这个园区约200个并发在线用户的规模为例,一台双核CPU、2GB内存、40GB磁盘的服务器就够用了,实际运行时CPU空闲率常年保持在90%以上。真正要注意的是数据库这一层。用户的认证请求、会话记录、兑换码库存都会落库,数据库连接数如果不做缓冲,用户集中接入的时段容易出现"连接数已满"的报错。我的做法是在应用和数据库之间保留一层连接缓冲,把MySQL的max_connections从默认值调高到512,同时给常用的session表和voucher表加上索引,避免全表扫描拖慢认证响应。
软件栈方面,考虑到这是2016年的版本,PHP 5.6分支和MySQL 5.5/5.6系列是它最舒服的运行环境。如果你的服务器系统比较新,自带PHP 8.x,我建议还是用Docker把旧环境隔离起来,或者直接找一台跑老系统的机器,省得在兼容性上花时间。Web服务器我用的是Nginx加PHP-FPM,相比Apache并发能力更好,配置也不复杂。需要注意PHP-FPM里的cgi.fix_pathinfo必须设为0,这是Nginx解析PHP时的一个老坑,不关掉容易被人利用构造恶意请求。
2.2 网络拓扑与IP规划要点
部署前先把IP规划做清楚,后面能省掉大量排错时间。我当时的拓扑分成三个网段:管理网段192.168.10.0/24专门放Portal服务器、数据库、运维终端;用户网段172.16.20.0/24分配给WiFi客户端,由网关上内置的DHCP服务统一分配;外网则走网关的WAN口上行。认证网关放在用户网段和WAN之间,同时连接管理网段,这样它既能和Portal服务器内网通信,也能直接把用户流量送出去。
值得多说一句的是,Portal服务器的地址一定要用固定IP,不能走DHCP分配,否则网关上的UAM回调地址会跟着漂移。我见过不止一次有人在这里踩坑——Portal服务器地址变了,用户认证成功后网关找不到回调目标,表现为"认证页面能打开,但输完密码就卡住"。另外,用户网段的DNS也要确认能被正确下发,不少老设备在DNS解析失败时,会导致重定向页面加载异常。
3. 从零部署:OpenPortalServer安装与网关联动
3.1 源码部署的完整步骤
把安装过程拆成一步步来,方便照抄。以下命令基于Debian系Linux发行版,其他系统请对应调整。
# 1. 安装基础软件栈 apt-get update apt-get install -y nginx mysql-server php5-fpm php5-mysql php5-gd php5-curl # 2. 把OpenPortalServer源码解压到web目录 mkdir -p /var/www/html/openportal cd /var/www/html/openportal tar -zxvf /root/OpenPortalServer_V3.3.5.6_Stable.tar.gz# 3. 创建数据库并导入初始表结构 mysql -uroot -p CREATE DATABASE openportal DEFAULT CHARACTER SET utf8mb4; GRANT ALL PRIVILEGES ON openportal.* TO 'portal'@'localhost' IDENTIFIED BY 'yourpassword'; FLUSH PRIVILEGES; exit mysql -uportal -p openportal < /var/www/html/openportal/database/install.sql# 4. 修改应用配置文件 vi /var/www/html/openportal/config/config.php配置文件中重点检查三块:数据库连接信息(主机、库名、账号、密码)、系统对外访问的基础URL、以及和网关约定的共享密钥。共享密钥这一项千万别留默认值,安装后第一件事就是改掉它。密钥不一致会导致网关和Portal之间无法正常通信,认证逻辑直接失灵。
# 5. 配置Nginx站点并重启服务 vi /etc/nginx/sites-available/openportal ln -s /etc/nginx/sites-available/openportal /etc/nginx/sites-enabled/ nginx -t && systemctl restart nginx && systemctl restart php5-fpm上传目录和日志目录要确保web用户有写权限,否则兑换码导出和日志写入会报错。这一步如果忽略,很容易在后期遇到"功能按钮点了没反应"的怪异问题,实际上就是目录不可写,错误信息又被日志记录逻辑吞掉了。
3.2 与Coova Chilli网关的联动配置
Portal服务起来了,接下来要让网关把用户请求引导过来。我用的是Coova Chilli作为WiFi接入认证网关,它有一个重定向参数专门指定Portal服务器地址。找一下chilli的配置文件,通常是/etc/chilli.conf,把以下几项改掉:
# chilli.conf 关键参数 uamserver http://192.168.10.10/openportal/index.php uamsecret your-shared-secret uamhomepage http://192.168.10.10/openportal/index.php coaport 3799这里uamserver就是"认证页服务地址",uamsecret必须和Portal配置里的共享密钥保持一致。改完之后重启chilli服务,然后拿一台手机连上WiFi测试,正常的话手机会自动弹出认证页面。如果手机没有弹出页面,手动打开浏览器访问任意HTTP站点,同样会触发重定向。
3.3 首次进入管理后台必做的两件事
浏览器访问Portal服务器的管理地址,使用安装过程中创建的管理员账号登录。第一次登录后建议先做两件事:一是修改管理员密码,二是核对系统基础参数,包括组织名称、时区、日期格式、默认会话时长。当时区不对的时候,会产生一个很隐蔽的问题:兑换码的有效期计算错位,明明买了24小时券,用户用了20小时就提示过期了。这类问题排查起来特别费劲,所以基础参数一定要在开局阶段就检查到位。
4. 核心功能实战:认证、兑换码与限速
4.1 本地账号与RADIUS双通道认证
OpenPortalServer在认证环节支持两条路径。一条是纯本地账号,管理员在后台手动创建用户,设置密码、有效期、并发数、限速模板,适合用户规模不大、运营动作简单的场景。另一条是走RADIUS协议对接外部认证服务器,适合已经有统一账号体系的单位,比如校园网账号、企业内部账号。我的园区一开始用的本地账号,后期接入了统一认证平台,切换时只需要在后台把认证源改成RADIUS,填上RADIUS服务器地址和共享密钥,用户侧完全无感知。
两种方式各有适用场景。本地账号胜在零依赖,不担心RADIUS服务器挂掉导致全网认证瘫痪;RADIUS则赢在账号集中管理,适合跨设备统一认证。如果条件允许,我建议主用RADIUS、本地账号留作应急备份通道,两边都配置好,关键时刻能救急。切换认证源的操作在后台几分钟就能完成,但要特别注意切换后一段时间内不要关闭旧通道,因为在线用户的会话续期请求可能还在走旧的RADIUS服务器,提前关掉会造成一批用户被动离线。
4.2 兑换码批量生成与生命周期管理
兑换码是公共WiFi运营里用得最频繁的功能。客人到店消费后,收银台把小票上的验证码给客人,客人输入后获得对应时长的上网权限。后台生成兑换码时可以设置多个参数:有效期(比如24小时、7天、30天)、同时在线数(防止一个码多个人同时使用)、限速模板、码位长度和批次数量。我的习惯是一次生成500个码,打印成A5小票,盖一个店章分发到各个收银点。
这里有个实操细节要提醒:生成兑换码时尽量选择字母数字混合且排除易混淆字符,像0和O、1和I这些直接去掉,不然客人输错几次就会烦躁,前台客服的解释成本很高。另外,后台要定期清理已经过期的兑换码和用户会话,虽然这个版本不会主动做自动清理,但可以写一个cron脚本每周跑一次清理,能明显降低数据库的膨胀速度。
4.3 带宽限速与在线用户管控
公共WiFi最怕有人开着下载工具把整个出口带宽吃满。OpenPortalServer的限速功能可以针对每个用户会话单独设置上行和下行速率。我当时给普通客人设置的是下行4Mbps、上行2Mbps,足以应付刷视频和网页浏览,又不会影响其他人。管理后台能实时看到在线用户列表,包括IP地址、MAC地址、接入时长、已用流量,对于异常占用资源的用户,可以直接在后台踢下线。
在线用户管控还需要设置两个超时参数:会话空闲超时和总时长上限。空闲超时我设置的是30分钟,超过30分钟没有流量动作,会话自动回收,避免大量僵尸会话挤占并发额度。总时长上限就是客人购买的券时长,到点自动断线,需要续费只能再购新码。
4.4 认证门户页面定制要点
门户页面是客人第一次接触到系统的界面,直接影响到品牌形象和信任度。OpenPortalServer把页面模板放在了独立的模板目录里,编辑起来比较友好。我在原有模板基础上改了三个地方:把Logo和配色换成园区的VI样式;在一级页面上加入服务条款的勾选项,勾选后才能点连接按钮;在认证成功页面上加了一段欢迎文案和WiFi使用规范。
改模板时需要特别注意一点:表单的提交地址和隐藏字段绝对不能动。这些字段承载着系统回传给网关的加密信息,一旦被误删,用户就算输入了正确的兑换码也过不了网关校验。我建议改模板前先用Git做一个版本标记,万一改坏了可以快速回滚。
5. 上线运营中的问题排查实录
5.1 用户连上WiFi却跳不出认证页
这个问题在测试阶段和上线初期出现频率最高。排查路径一般为:先确认网关上的uamserver地址是否可达,用一台连在用户网段的设备直接访问这个地址,如果打不开就先查网段路由;然后确认网关是否把HTTP流量拦截并重定向,很多情况下是chilli服务没有正常启动,重启一下即可;最后检查用户的浏览器是否缓存了旧的页面状态,换一个无痕窗口测试就能排除。
还有一个容易被忽略的原因:某些手机会强制走HTTPS连接,比如iOS的智能热点登录检测,它访问的是一个系统探测URL。如果网关没有配置针对这类探测流量的特殊处理,用户就会卡在"正在检测网络连接"的状态。解决办法是在网关上把这类探测域名加入允许列表,让它们无需认证即可访问,或者直接放行系统的网络检测请求。
5.2 认证成功但依然打不开网页
这种症状通常意味着问题出在认证后的授权环节,而不是认证本身。优先检查网关和Portal之间的共享密钥是否一致,密钥不匹配时,Portal认为已经通知网关放行了,但网关校验回调签名失败,拒绝放行。其次检查会话记账是否正常,如果RADIUS记账请求超时,网关可能一段时间后把已放行的会话强制断开。第三,确认用户的IP在认证前后没有发生变化,如果DHCP租期过短导致中途换IP,认证会话会跟着失效。
当时我处理过一个典型案例:用户输完验证码,页面显示"连接成功",但浏览器一直转圈打不开任何网站。排查下来是网关的MASQUERADE规则少了一条针对用户网段的条目,导致用户流量出了内网之后再也没有回来。这类网络层的问题,用tcpdump抓包看流向很快就能定位。
5.3 兑换码泄漏与并发滥用的对策
运营中最不想见到的情况就是兑换码被批量转卖或者超并发使用。单码并发限制可以在生成批次时设置,但防不住有人用一个号的截图发给多个人。我后来做的补救措施是:在后台定期筛查短期内登录次数异常的兑换码,发现单个码在多个IP段反复登录就直接停用;同时对短时券设置每日发放上限,控制风险敞口。一旦发现大范围异常,还可以在网关侧临时把认证模式切换成"必须使用本地账号",只对内部人员放行,等兑换码批次清理完再恢复正常模式。
5.4 时间不同步引发的玄学故障
如果你发现用户会话时长忽长忽短、兑换码偶尔提前失效、RADIUS认证时通时不通,先别急着怀疑代码,检查一下所有服务器和网关的时间是否同步。NTP时间偏差一旦超过RADIUS协议的容忍范围,就会产生各种难以解释的间歇性故障。我把Portal服务器、数据库、网关全部加入同一组NTP对时源,终于在一次排查中发现数据库时间偏差已经达到好几十秒,调整同步之后问题彻底消失。建议部署时就把NTP层级结构一次性搭好,后面会省心很多。
6. 写在最后:这套老版本带给我的几点启发
6.1 运维上的三点坚持
用OpenPortalServer V3.3.5.6这套方案整整两年,我的体会是:开源项目只要找准定位,稳定性和可用性完全不输商业产品,关键在使用者愿不愿意花时间把基础配置做扎实。运维层面有几点想特别强调。第一,数据库备份是生命线,我用的是每天凌晨全量备份加binlog增量备份的策略,有一次误操作删了三个月前的兑换码数据,靠备份恢复了绝大部分,损失被控制在可接受范围。第二,升级要克制,这个版本在现有场景里跑得好好的,就没有必要因为新版本发布就立刻迁移,新功能带来的收益如果小于迁移风险,按兵不动就是最好的策略。第三,所有登录和操作记录要保留足够长时间,当出现兑换码纠纷或用户投诉时,这些日志是定位问题的第一手依据。
6.2 一个受用至今的回归测试习惯
最后分享一个小技巧:每次调整门户页面或认证参数之前,先在测试环境用脚本模拟一次完整的认证流程,把兑换码生成、提交认证、回调确认这几步跑通,再推到生产环境。我写过一套基于curl的简易回归脚本,每次改动后跑一遍,基本能提前挡住九成以上的低级错误。这套流程后来被我用在了其他所有运维项目上,算是这个老版本送给我的一项额外收获。如果你也在维护类似的认证门户系统,真心建议把这个习惯建立起来,它不会占用太多时间,但在关键时刻真的能救命。
本文还有配套的精品资源,点击获取