利用Wifidog实现微信wifi连接以及自写认证服务器

前言

大家如果有用公共场合wifi的习惯,想必都有过如下的体验。


这就是利用微信身份来进行wifi连接认证,主要目的是商家为了吸引顾客,推广其公众号。别的也不多说,下面就来讲一讲怎么实现这样的wifi认证方式。

本篇文章要讲的是portal型路由设备(具体就是OpenWrt路由)的改造实现。在进行改造之前请务必要看微信公众平台开发文档的微信连wifi,后面提到的相关设涉及微信公众平台开发的相关术语、参数不再一一解释。


Wifidog的原理

下图是在Wifidog的wiki网站上截取的wifidog认证流程图,网址为点击打开链接

wifidog由两部分组成,一个是运行在路由器上的程序,另一部分是运行在认证服务器上的程序。

wifidog的认证流程大致是:

1.首先,用户的终端可以连接上wifi,然后发起访问网站的请求,如www.baidu.com;

2.网关根据防火墙规则,将用户的请求重定向到本地端口(wifidog的监听端口);

3.网关将用户的访问重定向到认证服务器上的认证页面;

4.认证服务器返回登录页面至用户;

5.用户再向认证服务器提供凭据,如用户名和密码;

6.认证服务器根据用户提供的凭据来确定用户是否符合要求,是否可以上网;

7.如果符合要求,认证服务器将用户的访问重定向至路由器的网关并携带标识token;

8.网关向认证服务器确认用户信息;

9.如果符合要求,服务器向用户返回登录成功页面;

10.用户就可以上网了。

WifiDog在openwrt上的安装和配置

我的路由器型号是:TP-LINK的TL-941N V4/V5版。

安装的openwrt是:openwrt-ar71xx-generic-tl-wr941nd-v4-squashfs-sysupgrade(先刷openwrt-ar71xx-generic-tl-wr941nd-v4-squashfs-factory)。

我是先装了luci,然后查到了这个路由器的wifidog版本的下载地址,然后通过opkg install安装的(由于这款路由器的FLASH只有4M,装了luci以后就不能装wifidog了,所以又刷了一次系统,只安装了wifidog,然后通过windows下的putty这个ssh工具来访问openwrt路由器。)。

首次访问路由时,可通过telnet工具,然后通过passwd,设置路由器的密码,之后就可通过ssh访问了。

Wifidog的配置如下所示:

[plain]  view plain  copy
  1. GatewayID default  
  2. GatewayInterface br-lan  
  3. GatewayAddress 192.168.1.1  
  4. ExternalInterface br-lan  
  5. AuthServer{  
  6. Hostname justyoung.com(也可以是域名)  
  7. SSLAvailable no  
  8. HTTPPort 8080(默认是80)  
  9. Path /wifidog/(文件名的两边都要加‘/’号,除非是根目录,只要一个‘/’)  
  10. LoginScriptPathFragment login.php/? (这里把第一次登陆时重定向的位置也修改了,加了一个后缀,默认是login/?,)   PingScriptPathFragment ping.php/?  
  11. PortalScriptPathFragment portal.html?  
  12. AuthScriptPathFragment auth.php/?  
  13. ...  
  14. 可以有多个AuthServer,Wifidog会从第一个往后找,直到找到可用的认证服务器为止。  

这段代码首先根据mac地址来判断登录用户是否合法,如果合法则直接重定向至192.168.1.1:2060/wifidog/auth?token=XXX,如果是不合法,则重定向至欢迎页面,引导用户获得认证。

Wifidog与AuthServer的交互

这里特别提一下,我发现在openwrt的download网站里,ar71xx有两个版本的wifidog,所以请根据需要确定好opkg的源,一个是2009版的wifidog,一个是2013版本的wifidog,我用的源是http://downloads.openwrt.org/snapshots/trunk/ar71xx.nand/packages/packages/,这个源的wifidog版本是wifidog_20130917-440445db60b0c3aff528ea703a828b0567293387_ar71xx,它们在login处有一个区别,后面会提。

先介绍一下wifidog与Auth服务器的交互协议:

1.首先是重定向,在首次登陆时,用户访问的url会被重定向到如下的地址:

login/?gw_address=%s&gw_port=%d&gw_id=%s&url=%s(2009版本的wifidog)

login/?gw_address=%s&gw_port=%d&gw_id=%s&mac=%s&url=%s(2013版本的wifidog)

这里有一个版本的问题,即2009的wifidog在重定向时不会在链接中带上mac参数,而2013版本的wifidog是会带上的,所以这里需要根据自己的应用特别注意。在用户首次连接路由上网时,它访问的url会被定向到login页面,并带上如上所述的参数,我们可以利用这些参数做生成token或其它一些判断等。而通常情况是在login中向用户返回通过wifi认证的方法,如带有用户名和密码的登录页面等。

2.用户认证协议:

auth_server:/auth/auth.php?stage=%s&ip=%s&mac=%s&token=%s&incoming=%s&outgoing=%s

一般情况下,认证服务器auth_server会根据用户输入的信息生成一个token,然后将用户重定向到wifidog的监听端口上,这个端口的默认地址为:192.168.1.1:2060/wifidog/auth?token=%s,wifidog得到这个token后,将其发送到auth_server认证服务器上进行认证。如果认证通过,auth_server返回“Auth: 1”,认证未通过则返回“Auth: 0”。具体参数如下。

0 - AUTH_DENIED - User firewall users are deleted and the user removed.6 - AUTH_VALIDATION_FAILED - User email validation timeout has occured and user/firewall is deleted1 - AUTH_ALLOWED - User was valid, add firewall rules if not present5 - AUTH_VALIDATION - Permit user access to email to get validation email under default rules-1 - AUTH_ERROR - An error occurred during the validation process

认证服务器通过获取以上链接的参数可以判断这个用户是否合法等。这个链接是认证服务器用来判断首次登陆的用户是否合法和正在连接的用户是否可以继续访问链接的方法。每隔一段时间,wifidog会向认证服务器发送信息,即通过如上所示的链接发送信息,通过这些参数,可以看到某个客户的上传流量、下载流量、mac地址、ip地址、token和、ip和stage。stage可能是两个参数,分别是counters或login。第一次登陆验证时,stage=login,其它时候stage=counters。

3.Ping协议

http://auth_sever/ping/?gw_id=%s&sys_uptime=%lu&sys_memfree=%u&sys_load=%.2f&wifidog_uptime=%lu

wifidog会向认证服务器发送一些信息,来报告wifidog现在的情况,这些信息是通过Http协议发送的,如上的链接所示,参数大概如字面意思,没仔细研究过,而作为认证服务器,auth_server应回应一个“Pong”。

4.认证成功后的跳转

portal/?gw_id=%s

在认证成功后,wifidog会将用户重定向至该页面。

5.若验证失败,则会根据失败原因跳转至如下页面

gw_message.php?message=denied

gw_message.php?message=activate

gw_message.php?message=failed_validation

注意一下,按照我对wifidog.conf的配置,在执行login时,相当于重定向至链接http://justyoung.com/wifidog/login.php?gw_id=XX....等等,其它执行的链接也是如此。

编写自己的WifiDog认证服务器

这次我使用的是php来编写auth_server服务器,因为这样比较简单。

1.首先是login.php

[php]  view plain  copy
  1. include './tool/MySQLHelper.php';  
  2. if (!empty($_GET["mac"])){  
  3.     $result = selectMacByToken($_GET["mac"]);  
  4.     if (!empty($result)){  
  5.         header("location: http://192.168.1.1:2060/wifidog/auth?token=".$result);  
  6.     }  
  7.     else {  
  8.         header("location: http://xxxxx/WelcomePage.php?mac=".$_GET["mac"]);  
  9.     }  
  10. }  
  11. else {  
  12.     header("location: http://xxxxx/WelcomePage.php?mac=".$_GET["mac"]);  
  13. }  
  14. ?>  

2.ping.php

[php]  view plain  copy
  1. echo "Pong";  
  2. ?>  

这里没有做额外的处理,只是简单地向wifidog回应一个Pong。

3.auth.php

[php]  view plain  copy
  1. // 后门  
  2. if ($_GET["token"] == "123"){  
  3.     echo "Auth: 1";  
  4.     return;  
  5. }  
  6.   
  7. if (!empty($_GET["token"]) && isset($_GET["token"])){  
  8.     //获取$result的过程//  
  9.     isValidate($result);  
  10.     //**后续处理**//  
  11.     return;  
  12. }  
  13. else if((!empty ($_GET["mac"])) && isset($_GET["mac"])){  
  14.     //**获取result*//  
  15.     $result = isSubscribeByMac($_GET["mac"]);  
  16.     isValidate($result);  
  17.     return;  
  18. }  
  19. else  
  20. {  
  21.     echo "Auth: 0";  
  22. }  
  23.   
  24. // 输出是否合法.  
  25. function isValidate($result){  
  26.     if ($result == 1){  
  27.         echo "Auth: 1";  
  28.     }  
  29.     else {  
  30.         echo "Auth: 0";  
  31.     }  
  32. }  
  33. ?>  

这里根据一些参数来获取$result,从而决定是否允许认证。

4.welcomePage.php以及Portal.html就不一一列举了。


微信wifi认证用户操作流程

简单的讲(无技术实现),通过微信连wifi的流程(手机端):

  1. 用户连上无密码的热点,请求一个链接
  2. 路由器返回个用户一个portal页面。
  3. 用户在portal页点击微信连WiFi按钮,唤起手机微信。
  4. 微信显示提供热点的公众号,用户点击立即连接后向开发者的认证服务器进行认证。
  5. 认证通过,微信提示连接成功并转跳公众号预设的主页。

对于用户而言,前面是TA要走的流程,对于开发者而言,要搞清楚每一步路由器、微信、认证服务器这三者到底做了什么,之间有什么关系。要想清楚这些,首先需要了解wifidog。

wifidog认证处理流程

wifidog的介绍就省略掉,相信看这篇文章的人都知道,直接进入正题。wifidog的认证有五种向认证服务器发送的协议,一种自己接受协议,虽说是协议,不过是不同的URL罢了。
例如:192.168.1.2:9408/login/?mac=xx&gw_id=**

协议 默认路径 作用
Login login/? 用于登录时
Auth auth/? 用于初次认证和心跳认证
Ping ping/? 用于路由器心跳
Portal portal/? 登录成功跳转
Msg gw_message.php? 用于出错时用于展示给用户信息

PS:官方画的这个图真的好奇葩,委屈下看官的脖子。或者可以转动屏幕,最好发到手机上看。

下面对这个流程图做个解释:

  1. 客户端发送一个请求,请求被转发到认证服务器(AuthServer)的Login协议的URL上。这个过程中,具体情况是当客户端向路由器发送一个HTTP时,OpenWrt中的iptables预定义的规则(在wifidog启动时写入规则)将所有请求转发到wifidog监听的地址(默认端口是2060)。当然配置文件中可以设置白名单,保证白名单列表的正常连接。再接着wifidog将向客户端返回302重定向,location的url如下,客户端将请求这个地址。

     
             
    1
    2
    3
    4
    5
     
             
    http://auth_server/login/?
    gw_id=[GatewaylD, default: "default]&
    gw_address=[GatewayAddress, internal IP of router]&
    gw_port=[GatewayPort, port that wifidog Gateway is listening on]&
    ur1=[user requested url]

    在配置文件中如果不设GatewayID,wifidog会将GatewayInterface的mac地址去掉冒号当做GatewayID。

    If none is supplied, the mac address of the GatewayInterface interface will be used,without the : separators

    如图前面流程图所示,每个参数都给出了解释,很清楚。当然url是没有换行的。

  2. 服务器收到login请求后,返回一个登录页面,用户提交表单(假如有的话)。

  3. 服务器处理表单,保存表单信息,生成一个token。向客户端返回302重定向,重定向的location是http://GatewayIP:GatewayPort/wifidog/auth?token=token,然后客户端向wifidog请求这个url。
  4. wifidog收到请求后取得token,并向AuthServer发送Auth请求,url如下所示,之后认证服务器取得参数并判断是否认证通过。

     
             
    1
    2
    3
    4
    5
    6
    7
     
             
    http://auth_server/auth/?
    stage=[login|counters]&
    ip=[client ip]&
    mac=[client mac]&
    token=[token]&
    incoming=[in data usage]&
    outgoing=[out data usage]
    • stage有两个值login表示登录认证,counters表示计数(心跳)认证。
    • incoming和outgoing是从连接开始到当前的总量,不是每次auth的值。

      AuthServer返回是否认证成功时,若成功,返回Auth: 1,若不成功,返回Auth: 0

  5. 若认证成功,wifidog会重定向客户端到portal页,url如:http://auth_server/portal/?gw_id=%s
  6. Ping协议:在每次wifidog启动的时候都会首先通过ping协议判断某认证服务器(可以有多个)是否在线,如果是Down状态就会尝试下一个。url如下:

     
             
    1
    2
    3
    4
    5
    6
     
             
    http://auth_sever/ping/?
    gw_id=%s&
    sys_uptime=%lu&
    sys_memfree=%u&
    sys_load=%.2f&
    wifidog_uptime=%lu
  7. 若验证失败,则会根据失败原因跳转至如下页面

     
             
    1
     
             
    gw_message.php?message=[denied|activate|failed_validation]

加入微信认证的流程

要想比较容易的理解这个流程,需要仔细阅读Wi-Fi硬件鉴权协议接口说明



结合微信的认证流程图和前面wifidog的认证流程,我们应该会有一些如何结合两者的想法。

  1. 手机选择SSID,无密码,连接。
  2. 手机系统尝试打开一个URL来确定网络可用(或者手动打开一个链接),路由的iptables转发请求到wifidog,wifidog将请求重定向到AuthServer,并加入参数。(参见Login协议)
  3. AuthServer返回微信portal页,页面中包含获得微信ticket的参数,包括appId,extend,timestamp,shopId,authUrl,mac,ssid,bssid,secretKey以及sign即对前面参数md5后的值。(参数后面再解释)
    注意此处有坑:
    • 此portal非wifidog认证中的portal,在结合微信的认证中wifidog的portal不会用到。
    • md5函数的结果中字母一定要是小写
  4. 点击portal页中的呼起微信连wifi按钮。
    • 首先portal页面中的js代码会向微信服务器发送Ajax请求,就是把要获取ticket的参数传过去。
    • 微信服务器验证参数和sign匹配,返回唤起微信所需要的ticket。
    • portal页面将通过微信的Schema:weixin://connectToFreeWifi/ticket=xx呼起微信。(有的浏览器不支持打开外部应用,最好用系统自带浏览器)
    • 微信连接微信服务器,核对ticket,成功则返回给微信用户的extend,openId,tid,然后微信会打开前置的连接wifi页(如图1中的连接前页)。
  5. 点击连接,向AuthServer发起请求认证,参数包括extend,openId,tid,AuthServer返回AC(Access Control)结果,如果通过要返回302重定向到wifidog的auth上,也就是wifidog认证流程中的第3步,重定向的location为http://GatewayIP:GatewayPort/wifidog/auth?token=token
  6. 微信请求重定向的链接,wifidog收到请求并取得token,发起向AuthServer的认证。若认证成功,微信显示连接成功页面。不成功则连接失败。
  7. 在连接成功页面点击完成转跳到公众号预设的home页。连接成功也也可以关注公众号。

对于关注公众号可以联网,取消关注断网:

  • 可以让用户提前关注公众号,在第5步时判断是否有粉丝关系,有即通过否则拒绝。这可以做到关注了公众号可以联网。
  • 要想做到取消关注即断网需要在认证成功之后的auth心跳时进行判断,具体怎么判断是否取消关注,是每次查询微信接口还是用户取消关注微信服务器会给你个回调,这个我还没研究。

关于实现的简单说明

把认证流程搞清楚之后,有开发能力的人基本就不用往下看了,本文也是针对于有一定开发能力的人,关于实现不会像教程一样详细,主要是提几个关键点。

wifidog客户端

安装wifidog

首先在OpenWrt中安装wifidog,如果是开发用的话安装repository中的就可以了。如果是用于生产的话需要对wifidog进行一些修改然后编译成自己需要的包,这里可以参考 WiFiDog 多线程优化思路。

 
       
1
2
 
       
opkg update
opkg install wifidog

开机自启 /etc/init.d/wifidog enable
启动服务 /etc/init.d/wifidog start

配置文件修改

wifidog默认配置文件在/etc/wifidog.conf,最好是要通读整个配置文件,也不是很多。

  1. 修改GatewayID,位置大概在15行
    GatewayID用来表示这个wifidog网关的,前面也提到过,注释掉着一行的话wifidog会取GatewayInterface的mac地址去掉冒号(separators)作为网关ID,如果一个路由上只运行一个wifidog这样做挺好,多个的话就需要自己设置。
    GatewayAddress和GatewayInterface一般按照默认就行,如果有需要自行设置。

     
             
    1
    2
     
             
    # GatewayID default
    # 这里很大一个坑,每个去掉注释的设置项一定要顶头开始,不能有空格,当然设置项的子项是要有缩进的。
  2. AuthServer项配置,位置大概在80行。

     
             
    1
    2
    3
    4
    5
    6
    7
     
             
    AuthServer {
    Hostname 192.168.66.186
    HTTPPort 9408
    SSLAvailable no
    Path /
    MsgScriptPathFragment gw_message/?
    }

    每项配置的具体作用和可选值在前面的注释中有。

  3. wifidog防火墙配置,在FirewallRuleSet中加入如下规则,位置大概在245行。

     
             
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
             
    # 放行微信
    FirewallRule allow tcp to wifi.weixin.qq.com
    FirewallRule allow tcp to dns.weixin.qq.com
    FirewallRule allow tcp to short.weixin.qq.com
    FirewallRule allow tcp to long.weixin.qq.com
    FirewallRule allow tcp to szshort.weixin.qq.com
    FirewallRule allow tcp to mp.weixin.qq.com
    FirewallRule allow tcp to res.wx.qq.com
    FirewallRule allow tcp to wx.qlogo.cn
    FirewallRule allow tcp to minorshort.weixin.qq.com
    FirewallRule allow tcp to adfilter.imtt.qq.com
    FirewallRule allow tcp to log.tbs.qq.com
    # 放行Apple
    #FirewallRule allow tcp to apple.com
    #FirewallRule allow tcp to icloud.com

最小化修改就是以上三个地方,其它配置项建议也要看看,如果有需要的时候知道改哪里。

通过 wifidog -h 命令查看用法,一般在开发的时候使用 wifidog -f -d 7即可。

认证服务器

认证服务器就是处理wifidog的五种协议,如果只是简单的认证,用php、python或者其他的脚本语言会更容易实现。因为我需要别的一些处理,所以我选择JAVA来处理。

代码的话不准备在这里写了,放在Github,不过现在代码写的太烂,完成度太低,还没有push,先放链接 fliapingWifi-auth

参考文章

  • [wifidog-gateway]-Github
  • Wi-Fi硬件鉴权协议接口说明
  • wifidog安装以及自写wifidog认证服务器

你可能感兴趣的:(利用Wifidog实现微信wifi连接以及自写认证服务器)